将光盘上的点随机化


14

我读过某处的圆圈,现在才了解了光盘(这实际上是一个非常普遍的概念),并想到了代码高尔夫。

您的任务是将半径为1 的光盘上的一个点/几个点随机化。

规则:

  • 所有点的产生概率必须相等
  • 必须使用浮点坐标;最低要求是两位小数(例如点(0.12, -0.45)(0.00, -1.00)有效)
  • 如果程序实际显示边界圆和其中生成的点,则您将获得-20个字节。坐标仍然必须有效但不能显示,并且生成的图像大小必须至少为201 x 201像素
  • 如果程序将要生成的点数作为标准输入的输入,则得到-5字节
  • 如果您决定不绘制边界圆和点,则程序必须输出在格式(x, y)或格式上生成的点。(x,y)标准输出上
  • 如果您决定将生成的点数作为输入,但不作图,则您的程序必须以上述格式输出所有随机化的点,中间必须有一个空格

以字节为单位的最短提交获胜!


1
@sweerpotato是的,请指定圆上和圆上的所有点均有效。我没有意识到你的意思是两个。此外,这个问题似乎比普及竞赛更适合应对代码高尔夫挑战,但这只是我的观点。
科尔2015年

5
以创新的方式进行XYZ ”是经典的Bad Popcon Question™。一个人认为创造力是另一个人认为明显的方式。
彼得·泰勒

出于好奇,为什么要对图块输出201x201像素?
JohnE 2015年

@JohnE我建议使用201x201像素,因为它与所需的2个小数位精度相匹配
trichoplax

我们可以将坐标输出为复数吗?例如:0.3503082505747327+0.13499221288682994j
奥尔普

Answers:


5

Pyth,26-5 = 21字节

VQp(sJ*@OZ2^.n1*yOZ.l_1)eJ

获取在stdin上生成的坐标数,并在stdout上将其输出,如下所示:

(-0.5260190768964058, -0.43631187015380823)(-0.12127959509302746, -0.08556306418467638)(-0.26813756369750996, -0.4564539715526493)

使用类似于@MartinBüttner的策略,生成极坐标和半径,除了使用复杂的幂运算。


您可以删除p,不是吗?它只是将输出更改为单独的行。
PurkkaKoodari 2015年

@ Pietu1998这是不允许的,请参阅主要问题的评论。
orlp 2015年

哦那好吧。
PurkkaKoodari

16

CJam,28 27字节

PP+mr_mc\ms]1.mrmqf*"(,)".\

此解决方案不是基于拒绝的。我在极坐标中生成点,但是半径的分布不均匀,以实现点的均匀密度。

在这里测试。

说明

PP+     e# Push 2π.
mr_     e# Get a random float between 0 and 2π, make a copy.
mc\     e# Take the cosine of one copy and swap with the other.
ms]     e# Take the sine of the other copy and wrap them in an array.
        e# This gives us a uniform point on the unit circle.
1.mr    e# Get a random float between 0 and 1.
mq      e# Take the square root. This is the random radius.
f*      e# Multiply x and y by this radius.
"(,)".\ e# Put the resulting numbers in the required format.

为什么行得通?考虑半径r和(较小)宽度的窄环dr。面积大约为2π*r*dr(如果环面狭窄,内​​外圆周几乎相同,则可以忽略其曲率,因此可以将该面积视为矩形的面积,该矩形的周长为边长,宽度为环)。因此,面积随半径线性增加。这意味着我们还希望获得随机半径的线性分布,以实现恒定的密度(在半径的两倍处,要填充的面积是两倍,因此在此处需要两倍的点)。

如何生成从0到1的线性随机分布?首先让我们看一下离散情况。说,我们有4个值的所希望的分布,像{0.1, 0.4, 0.2, 0.3}(即,我们希望1为4倍一样常见0,和两倍一样普遍2;我们希望3三倍一样常见0):

enter image description here

如何选择具有期望分布的四个值之一?我们可以将它们堆叠起来,在y轴上选择介于0和1之间的均匀随机值,然后在该点选择线段:

enter image description here

但是,可以通过另一种方式可视化此选择。取而代之的是,我们可以用累积的值替换分布的每个值:

enter image description here

现在,我们将此图表的顶线视为一个函数,f(x) = y并将其求反以得到一个函数,可以将其应用于以下形式的均匀随机值:g(y) = f-1(y) = xy ∈ [0,1]

enter image description here

很酷,那么如何利用它来生成半径的线性分布?这是我们想要的分布:

enter image description here

第一步是累积分布值。但是分布是连续的,因此我们不对所有先前的值求和,而是对0to 进行积分r。我们可以通过分析轻松地解决该问题:。但是,我们希望对此进行归一化,即将其乘以一个常数,以使给出的最大值,因此我们真正想要的是:0r r dr = 1/2 r21rr2

enter image description here

最后,我们将其求反以得到一个函数,该函数可以应用于中的一个统一值[0,1],我们可以再次对其进行解析:这就是r = √y,其中y随机值是:

enter image description here

这是一项相当有用的技术,通常可以用来精确地生成简单的分布(它适用于任何分布,但对于复杂的分布,则最后两个步骤可能必须在数值上求解)。但是,我不会在生产代码的这种特殊情况下使用它,因为平方根,正弦和余弦非常昂贵:使用基于拒绝的算法平均起来要快得多,因为它只需要加法和乘法。


1
很好的解释!
sweerpotato

2
Mmm图片:D
Beta衰减

12

Mathematica, 68岁 44-20 = 24字节

非常感谢David Carraher告诉我有关信息RandomPoint,它节省了24(!)个字节。Mathematica确实具有所有功能的内置功能。

Graphics@{Circle[],Point@RandomPoint@Disk[]}

这将绘制有资格获得奖金的点和边界圆:

enter image description here

结果是矢量图像,因此201x201像素的尺寸规格实际上没有意义,但是默认情况下,它的渲染尺寸大于该尺寸。


怎么Graphics[{Circle[], Point@RandomPoint@Disk[]}]
DavidC 2015年

别客气。另外,要保存1个字节...Graphics@{Circle[], Point@RandomPoint@Disk[]}
DavidC

@DavidCarraher非常感谢!:)
Martin Ender 2015年

我不知道Mathematica语法,但是可以肯定的是,您可以通过删除,?后的空格来节省另一个字节。
蓬松的

@fluffy我已经在发布的版本中做了
Martin Ender

9

果酱 31个 26字节

{];'({2dmr(_}2*@mhi}g',\')

这是通过在边长为2的正方形中重复生成随机点并保持第一个落在单元盘内的方式进行的。

感谢@MartinBüttner打高尔夫球3个字节!

CJam解释器中在线尝试。

怎么运行的

{                  }g       Do:
 ];'(                         Clear the stack and push a left parenthesis.
     {      }2*               Do twice:
      2dmr                      Randomly select a Double between 0 and 2.
          (_                    Subtract 1 and push a copy.
               @              Rotate the copy of the first on top of the stack.
                mh            Compute the Euclidean norm of the vector consisting
                              of the two topmost Doubles on the stack.
                  i           Cast to integer.
                            If the result is non-zero, repeat the loop.
                     ',\    Insert a comma between the Doubles.
                        ')  Push a right parenthesis.

8

iKe53 51字节

没什么特别的,但是我想我们应该至少有一个图形解决方案:

,(80+160*t@&{.5>%(x*x)+y*y}.+t:0N 2#-.5+?9999;cga;3)

plot

在浏览器中尝试一下。

编辑:我可以通过应用@MartinBüttner的方法修改极坐标的分布来剃除两个字节。我认为它也更加直接:

,(80*1+(%?c){x*(cos y;sin y)}'6.282*?c:9999;cga;3)

3
如果您还绘制边界圆,则有资格获得-20。
orlp 2015年

1
iKe具有基于栅格的绘图模型,因此该要求相当不公平。我认为渲染一个近似的圆也要花费超过20个字符。
JohnE 2015年

7

Perl,59字节

while(($x=1-rand 2)**2+($y=1-rand 2)**2>1){};print"($x,$y)"

这只是一个简单的解决方案,在正方形中生成点,并拒绝太远的点。我唯一的打高尔夫球技巧是将条件包括在内。

编辑:在打高尔夫球的过程中,我发现了一种有趣的方式在上打印随机点。

use Math::Trig;$_=rand 2*pi;print"(",sin,",",cos,")"

7

八度,24 53-20 = 33字节

polar([0:2e-3:1,rand]*2*pi,[ones(1,501),rand^.5],'.')

生成501个等距的theta值和一个随机数,并将它们全部缩放为[0..2π]。然后为圆的半径生成501 1,再为该点生成一个随机半径,并取平方根以确保在光盘上均匀分布。然后将所有点绘制为极坐标。

enter image description here


这是分布的快速演示(不包含单位圆):

polar(2*pi*rand(99),rand(99).^.5,'.')

9801 Points


5

八度/ Matlab,74 64字节

拒绝方法,64个字节:

u=1;v=1;while u^2+v^2>1
u=rand;v=rand;end
sprintf('(%f,%f)',u,v)

直接方法,74个字节(感谢MartinBüttner帮助我纠正了两个错误):

t=rand*2*pi;r=1-abs(1-sum(rand(2,1)));sprintf('(%f,%f)',r*cos(t),r*sin(t))

5

R,99 95 81-20 = 79 75 61字节

symbols(0,0,1,i=F,asp=1,ylim=c(-1,1));points(complex(,,,runif(9),runif(9,-1)*pi))

使用复数构造从极坐标构造x / y。接受输入有点昂贵,并且可能有更好的方法可以做到这一点。的ylim xlim是保证整个圆圈作图和asp确保点在圆圈符号下显示。

感谢@jbaums和@flodel的节省

在这里尝试


runif(9,0,1)可以简化为runif(9)
jbaums 2015年

@jbaums,谢谢……我似乎总是忘记的一件事:)
MickyT 2015年

可以刮胡子14:symbols(0,0,1,i=F,asp=1,ylim=c(-1,1));points(complex(,,,runif(9),runif(9,-1)*pi))
flodel

@flodel非常感谢。
MickyT 2015年

另一个小小的节省:yli代替ylim
jbaums 2015年

4

处理/ Java 141字节20 = 121

201 * 201为最小尺寸的要求要求我输入该setup方法,因为Processing.org默认为200x200 :(

void setup(){noFill();size(201,201);}void draw(){float f=10,a=PI*2*random(),r=random();point(f+f*sin(a)*r,f+f*cos(a)*r);ellipse(f,f,f*2,f*2)}

我不知道允许处理/ java,整洁!
J Atkin

4

QBasic,138个字节-20-5 = 113

INPUT n
r=200
SCREEN 12
RANDOMIZE TIMER
CIRCLE(r,r),r
PAINT(r,r)
FOR i=1TO n
DO
x=RND*r*2
y=RND*r*2
LOOP UNTIL POINT(x,y)
PSET(x,y),1
NEXT

接受用户输入并绘制光盘和点。在QB64上测试。

这是一个非常基本的“投掷飞镖,保持坚韧不拔”的策略。问题在于,“算什么”不是通过数学方式确定的,而是通过图形方式确定的:将白色圆盘绘制在黑色背景上,然后拒绝随机生成的点,直到它们不是黑色为止。点本身以蓝色绘制(尽管很难分辨它们何时是单个像素,请单击图像放大)。


3

awk-95-5 = 90

{
    for(;$1--;printf"("(rand()<.5?x:-x)","(rand()<.5?y:-y)")")
        while(1<(x=rand())^2+(y=rand())^2);
}

由于我不太确定rand()<。5部分,因此使用以下脚本对此进行了一些分发测试:

BEGIN{ srand() }
{ 
    split("0 0 0 0", s)
    split("0 0 0 0", a)

    for(i=$1; i--; )
    {
        while( 1 < r2 = ( x=rand() )^2 + ( y=rand() )^2 );

        x = rand()<.5 ? x : -x
        y = rand()<.5 ? y : -y

        ++s[ x>=0 ? y>=0 ? 1 : 4 : y>=0 ? 2 : 3 ]

        ++a[ r2>.75 ? 1 : r2>.5 ? 2 : r2>.25 ? 3 : 4]
    }

    print "sector distribution:"
        for(i in s) print "sector " i ": " s[i]/$1

    print "quarter area distribution:"
        for(i in a) print "ring " i ":   " a[i]/$1
}

在我喝了一两次咖啡后,输入1e7可以得到这个结果:

1e7
sector distribution:
sector 1: 0.250167
sector 2: 0.249921
sector 3: 0.249964
sector 4: 0.249948
quarter area distribution:
ring 1:   0.24996
ring 2:   0.25002
ring 3:   0.250071
ring 4:   0.249949

我认为这还不错。

稍作解释:
经过一会儿的刻划,如果您想将圆盘分成相等面积的四个环,则必须切割的半径为sqrt(1/4),sqrt(1/2) )和sqrt(3/4)。由于我测试的点的实际半径为sqrt(x ^ 2 + y ^ 2),因此可以跳过平方根。1 / 4、2 / 4、3 / 4的“巧合”可能与M. Buettner先前指出的有关。


3

HPPPL,146(171-20-5)个字节

EXPORT r(n)BEGIN LOCAL R,A,i,Q;RECT();Q:=118.;ARC_P(Q,Q,Q);FOR i FROM 1 TO n DO R:=√RANDOM(1.);A:=RANDOM(2*π);PIXON_P(G0,IP(Q+Q*R*COS(A)),IP(Q+Q*R*SIN(A)));END;FREEZE;END;

10000点的示例(包括实际设备的计时单位为秒):

将光盘上的点随机化,定时

该函数本身被调用 r(n)。上图中的其余部分仅用于计时目的。

结果(光盘直径为236像素):

在此处输入图片说明

上面的版本不存储点坐标,因此我编写了一个带有两个参数的版本r(n,p)n是点的数量,p=0并将点返回到终端,p=1绘制点和光盘)(如果必须存储坐标)。此版本的长度为283(308-20-5)字节:

EXPORT r(n,p)BEGIN LOCAL R,A,j,Q,x,y;Q:=118.0;CASE IF p==0 THEN print() END IF p==1 THEN RECT();ARC_P(Q,Q,Q) END END;FOR j FROM 1 TO n DO R:=√RANDOM(1.0);A:=RANDOM(2*π);x:=R*COS(A);y:=R*SIN(A);CASE IF p==0 THEN print("("+x+", "+y+")") END IF p==1 THEN PIXON_P(G0,IP(Q+Q*x),IP(Q+Q*y)) END END;END;FREEZE;END;

非高尔夫版本:

EXPORT r(n,p)
BEGIN
LOCAL R,A,j,Q,x,y;
  Q:=118.0;
  CASE
    IF p==0 THEN print() END
    IF p==1 THEN RECT();ARC_P(Q,Q,Q) END
  END;
  FOR j FROM 1 TO n DO
    R:=√RANDOM(1.0);
    A:=RANDOM(2*π);
    x:=R*COS(A);
    y:=R*SIN(A);
    CASE
      IF p==0 THEN print("("+x+", "+y+")") END
      IF p==1 THEN PIXON_P(G0,IP(Q+Q*x),IP(Q+Q*y)) END
    END;
  END;
  FREEZE;
END;

终端输出r(10,0)

随机化光盘终端输出上的点

r(10,1) 显示带有点的光盘,如上图所示。


2

JavaScript,75个字节

基于拒绝:

do x=(r=()=>4*Math.random()-2)(),y=r()
while(x*x+y*y>1)
alert(`(${[x,y]})`)

直接方法(80字节):

alert(`(${[(z=(m=Math).sqrt((r=m.random)()))*m.sin(p=m.PI*2*r()),z*m.cos(p)]})`)

2

Python,135130字节

from random import*
def r():return uniform(-1,1)
p=[]
while not p:
    x,y=r(),r()
    if x**2+y**2<=1:p=x,y
print'(%.2f, %2f)'%p

删除了@ jimmy23013的建议的**0.5感谢(因为它是一个单位圆,所以我现在要检查(x,y)与(0,0)之间的距离平方是否等于1 2,这是相同的东西)。

这也使我得以删除括号。


我认为您不需要**0.5
jimmy23013 2015年

@ jimmy23013谢谢!删除。
JF 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.