一条链上的狗


31

我从阁楼的窗户向外望去,到邻居家的院子里。他们的狗被拴在院子中央的一个柱子上。狗在院子里跑来跑去,但总是在链的尽头,所以它最终在泥土中留下了痕迹。通常,这条轨道是完美的圆形,但是我的邻居的院子里还有其他一些杆子,狗的链子就被抓住了。每次狗链碰到一根杆时,狗都会绕着新的杆旋转,而无论链的长度如何,半径都会保持不变。由于极,狗和链条的宽度均为零(我的邻居是数学家),因此链条可以无限地绕杆子缠绕,而圆半径不会缩短。如果链条在其路径中,则狗也可以通过链条(但不能穿过项圈)。观察了一段时间后,我决定编写一些代码来模拟邻居的狗。该代码将获取狗被拴在其上的中心杆的位置,邻居院子中其他杆的位置,链的长度以及狗的起始位置,并输出一个图,以指示狗已经把草磨破的小路。您可以假定以下各项的任何组合都是常数(因此不将其作为输入):

  • 狗拴在一起的电线杆的位置

  • 链长

  • 狗的起始位置

太阳正在升起,所以被窗户照亮的阁楼地板上的空间正在缩小,给我的编写代码的空间越来越少。请尽量减少代码的字节数,以便我有足够的空间在阁楼上草拟该代码。

测试用例

在这里,我假设那只狗从链条所在的极点(红点)向南3个单位开始0,0。为了清楚起见,我已经在极点处指示了点,您无需在输出中包括它们。

Poles at 1,2 -1,2

测试1

Poles at 0,.5

测试2

Poles at 0,1 1,1 -2,1 -1,-.5

测试3

Poles at 0,1 1,1

测试4


输出是{0,-.5}什么?
Kritixi Lithos

@KritixiLithos其输出{0,.5}垂直翻转,没有最大的圆圈。狗基本上开始被第二杆抓住。
小麦巫师

由于浮点问题,我的程序在最后一个测试用例(字符串长度为99.99999)中在(1,1)周围画了一个圆。这个可以吗?
Kritixi Lithos

狗沿顺时针和逆时针方向运行,但从固定点开始?
user202729

3
“太阳升起了,我的阁楼地板上被窗户照亮的空间正在缩小,这给我越来越少的空间来编写我的代码” +1
狮子座

Answers:


11

使用matplotlib的Python 3,457字节

from cmath import*
from matplotlib import pyplot as g,patches as i
def x(p):
 p+=[0];d=180/pi;a=2;h=g.gca();h.set_xlim(-5,5);h.set_ylim(-5,5)
 while a:
  a-=1;c=0;y=3;z=-pi/2
  while 1:
   s=[n for n in p if abs(n-c)<=y and n!=c]
   if not s:h.add_patch(i.Arc((c.real,c.imag),y*2,y*2));break
   n=[max,min][a](s,key=lambda n:(z-phase(n-c))%(2*pi));l,r=polar(n-c);h.add_patch(i.Arc((c.real,c.imag),y*2,y*2,[z,r][a]*d,0,[r-z,z-r][a]*d));y-=l;z=r;c=n
 g.show()

因为您的邻居是数学家,所以我假设您邻居的花园占据了复数域,因此花园中对象的任何坐标都是复数。要使用此功能,因此应向其传递一个复数列表,以表示邻居花园中两极的位置。选择了默认的坐标系表示形式,其中右侧是正实数,向上是正虚数。这意味着示例变为:

x([2j+1,2j-1])
x([.5j])
x([1j,1+1j,-2+1j,-1-.5j])
x([1j,1+1j])

此外,该程序还假设以下情况:皮带束缚在点0上,皮带束长为3个单位,绘图区域以0为中心为10乘10。对于这些参数,结果与示例完全匹配,这就是结果的样子(对于最后一个示例):

x([1j,1 + 1j])

该算法非常简单,只需要一个条件就可以区分顺时针和逆时针搜索。算法的状态由当前旋转点和皮带碰到当前旋转点时皮带的方向/剩余长度定义。其工作方式如下:

  • 从碰撞集中滤除距离当前旋转点比剩余牵引带长度和当前旋转点更远的点。
  • 如果此集合为空,则在到达该臂的末端时,在该点周围绘制一个带有剩余皮带长度半径的圆。
  • 确定差异矢量和皮带方向之间的相位差最小/最大的点。这是皮带沿顺时针/逆时针方向分别击中的下一个点。
  • 根据这些向量绘制圆弧,获取皮带的长度,减去距离的大小,然后将皮带的方向设置为差异矢量的方向。更新旋转点并从头开始。

然后首先在顺时针方向执行此算法,然后重置状态并在逆时针方向执行该算法。该算法的简单性意味着程序字节数的大约一半用于绘图功能。如果删除了绘图例程,它将从程序大小中删除218个字节。

以下是未发布的版本,还包含调试代码,该调试代码还显示了点和皮带碰撞:

from cmath import pi, rect, polar, phase
from matplotlib import pyplot, patches
def x_ungolfed(points):
    degrees = 180/pi # conversions

    # add the center point to the collision points
    points.append(0.0)

    # configure plot area
    axes=pyplot.gca()
    axes.set_xlim(-5,5)
    axes.set_ylim(-5,5)

    # plot the points
    x, y =zip(*((p.real, p.imag) for p in points))
    axes.scatter(x, y, 50, "b")

    # first iteration is clockwise, second counterclockwise
    clockwise = 2
    while clockwise:
        clockwise -= 1

        # initial conditions
        center = 0 + 0j;
        leash_size = 3
        leash_angle = -pi / 2

        # initial leash plot
        leash_start = rect(leash_size, leash_angle)
        axes.plot([center.real, leash_start.real], [center.imag, leash_start.imag], "r")

        # search loop
        while 1:
            # find possible collission candidates
            candidates = [n for n in points if abs(n - center) <= leash_size and n != center]
            # if we reached the end, draw a circle
            if not candidates:
                axes.add_patch(patches.Arc(
                    (center.real, center.imag), 
                    leash_size*2, leash_size*2
                ))
                break
            # find the actual collision by comparing the phase difference of the leash angle vs the difference between the candidate and the current node
            new = (min if clockwise else max)(candidates, key=lambda n: (leash_angle - phase(n - center)) % (2 * pi))

            # convert the difference to polar coordinates
            distance, new_angle = polar(new - center)
            # draw the arc
            if clockwise:
                axes.add_patch(patches.Arc(
                    (center.real, center.imag),
                    leash_size * 2, leash_size * 2,
                    new_angle * degrees,
                    0,
                    (leash_angle-new_angle) * degrees
                ))
            else:
                axes.add_patch(patches.Arc(
                    (center.real, center.imag),
                    leash_size * 2, leash_size * 2,
                    leash_angle * degrees,
                    0,
                    (new_angle - leash_angle) * degrees
                ))
            # draw intermediate lines
            edge = rect(leash_size, new_angle) + center
            axes.plot([center.real, edge.real], [center.imag, edge.imag], "g")

            # perform updates: decrease remaining leash size, set new leash angle, move rotation center to the collision
            leash_size -= distance
            leash_angle = new_angle
            center = new

    # show the graph
    pyplot.show()

它产生的输出如下所示:

与上一张图片相同,但行数更多


+1是一个很好的解释,而且使我打高尔夫球的次数几乎增加了两倍!<s>我

7

处理3,815个833 835 876 879字节

@ZacharyT通过删除不必要的括号节省了两个字节

void settings(){size(600,600);}int i,w,x,n;float l,d,t,a,f,g,m,R,U;float[][]N,T;float[]S,p;void s(float[][]t){N=new float[t.length+1][2];N[0][0]=N[0][1]=i=0;for(float[]q:t)N[++i]=q;translate(w=300,w);noFill();pushMatrix();f(N,0,-w,w,1,0);popMatrix();f(N,0,-w,w,0,0);}float p(float a,float b){for(a+=PI*4;a>b;)a-=PI*2;return a;}void f(float[][]P,float x,float y,float L,int c,int I){l=2*PI;d=i=0;S=null;for(;i<P.length;i++){float[]p=P[i];g=atan2(y,x);m=atan2(p[1],p[0]);if(p(f=(c*2-1)*(g-m),0)<l&(t=dist(0,0,p[0],p[1]))<=L&I!=i){l=p(f,0);S=new float[]{g,m};d=t;n=i;}}if(S==null)ellipse(0,0,2*(L-d),2*(L-d));else{arc(0,0,L*2,L*2,p(S[c],S[1-c]),S[1-c]);R=cos(a=S[1]);U=sin(a);translate(d*R,d*U);T=new float[P.length][2];for(int i=0;i<T.length;T[i][1]=P[i][1]-d*U,i++)T[i][0]=P[i][0]-d*R;f(T,(L-d)*R,(L-d)*U,L-d,c,n);}}

像这样运行该程序:

void setup() {
    s(new float[][]{{0,100},{100,100},{-200,100},{-100,-50}});
}

(该函数s接受float[][])。这实际上是测试用例3,但要乘以100以适合窗口。

需要注意的几件事:

  • 该程序不会画极点
  • 图像显示为上下颠倒,因为在Processing的坐标系中,y轴的正方向向下
  • 因为处理使用浮点数,所以计算不是很准确,因此您可能会在图像中看到这一点。我问过OP这些浮点错误是否重要。
  • 窗口的大小是600像素乘600像素
  • 很小的输入坐标会使程序出错,因为堆栈pushMatrix()及其上的popMatrix()操作只能容纳32个矩阵。
  • 狗从(0,-300)开始,链从300像素长开始
  • 为了方便起见,以下图片已缩小

上述测试用例的样本输出。

在此处输入图片说明

如果要查看预设的输出,请在translate(w,w);in函数之后添加此行s

background(-1);scale(1,-1);fill(255,0,0);ellipse(0,0,25,25);fill(0);for(float[]q:N)ellipse(q[0],q[1],25,25);

这给了我们这个结果:

圈

脱节f()和解释

(还包含调试代码)

void f(float[][]points, float x, float y, float len, int c, int pindex) {
    print(asd+++")");
    float closest = 2*PI;
    float d=0,t;
    float[]stuff = null;
    int index = 0;
    for(int i=0;i<points.length;i++) {
        if(pindex != i) {
            float[]p = points[i];
            float originAngle = atan2(y, x);
            float tempAngle = atan2(p[1], p[0]);
            //println(x,y,p[0],p[1]);
            float diff = c<1?tempAngle-originAngle:originAngle-tempAngle;
            println("@\t"+i+"; x=\t"+x+"; y=\t"+y+"; tx=\t"+p[0]+"; ty=\t",p[1], diff, originAngle, tempAngle);
            if(p(diff) < closest && (t=dist(0,0,p[0],p[1])) < len) {
                println("+1");
                closest = p(diff);
                stuff = new float[]{originAngle, tempAngle};
                d=t;
                index = i;
            }
        }
    }
    if(stuff == null) {
        ellipse(0,0,2*(len-d),2*(len-d));
        println("mayday");
    } else {
        println("d angles",d,p(stuff[c],stuff[1-c],c), stuff[1-c]);
        //println(points[0]);
        arc(0, 0, len*2, len*2, p(stuff[c],stuff[1-c],c), stuff[1-c]);
        float angle = stuff[1];
        translate(d*cos(angle), d*sin(angle));
        println("Translated", d*cos(angle), d*sin(angle));
        println("angle",angle);
        float[][]temp=new float[points.length][2];
        for(int i=0;i<temp.length;i++){
            temp[i][0]=points[i][0]-d*cos(angle);
            temp[i][1]=points[i][1]-d*sin(angle);
            println(temp[i]);
        }
        println(d*sin(angle));
        pushMatrix();
        println();
        f(temp, (len-d)*cos(angle), (len-d)*sin(angle), (len-d), c, index);
        popMatrix();
        //f(temp, (len-d)*cos(angle), (len-d)*sin(angle), (len-d), 0, index);
    }
}

简而言之,该程序将发送两个“搜索器”,一个是逆时针方向,另一个是顺时针方向。如果链条足够长,这些搜寻器中的每一个都会找到最接近的极点并向其画一条弧,否则,它会画一个圆。一旦画出弧线,它就会向该极点发送另一个搜寻器,然后继续进行该过程。f()包含每个寻求者的过程。我越打高尔夫球,就会有更详细的解释。


您是否需要最后一个括号L-d
扎卡里

@ZacharyT我不知道我是怎么想念的,谢谢。
Kritixi Lithos

5

LOGO,305个 298 297 293字节

在FMSLogo上尝试代码。

定义一个函数draw(称为d),给定输入作为极坐标列表(例如)draw [[0 100] [100 100] [-200 100] [-100 -50][0 0]],将在屏幕上绘制结果。

要求:

  1. 初始绳索长度= 300像素。(因为3像素太小)
  2. [0 0]必须包含在极点列表中。如果调试代码(拉杆)已打开,则[0 0]必须是最后一项。
  3. 狗从坐标开始x=0, y=-300(如问题描述中所示)

可能的优化:

  1. -1字节如果例外情况下(狗运行成极)不需要通过更换为数学上正确>=>

高尔夫代码:

to f
op(if ?=pos 360 modulo :m*(180+heading-towards ?)360)
end
to x :m[:1 300]
home
forever[make 2 filter[:1>=u ?](sort :p[(u ?)<u ?2])invoke[pd
arc -:m*f :1
pu
if 360=f[stop]make 1 :1-u ?
lt :m*f
setpos ?]reduce[if f<invoke[f]?2[?][?2]]:2]
end
to d :p
copydef "u "distance
foreach[1 -1]"x
end

Ungolfed代码(;启动内联注释(用于解释),并:启动变量名):

to f
    op ifelse ? = pos 360 modulo :m*(180 + heading - towards ?) 360
end

to x
    home
    foreach :poles [pu setpos ? pd circle 5] ; debug code
    make "length 300 ; initial length of rope
    forever [
        make "tmp filter [:length >= distance ?] ; floating point error makes > and >= similar,  ~
            ; but >= is correct mathematically ~
            (sort :poles [(distance ?) < distance ?2])
         ; the last = longest element will be rotated
        invoke [
            pd
            arc -:m*f :length
            pu
            if 360=f [stop]
            make "length :length - distance ?
            lt :m*f
            setpos ?
        ] reduce [
            if f < invoke[f]?2 [?] [?2]
        ] :tmp ; apply to use ? instead of :pos
    ]
end

to draw :poles
    foreach [1 -1] [[m]
        x
    ]
end

1

Python 2 + PIL,310字节

from PIL import Image
from cmath import*
I,_,X,P=Image.new('1',(300,300),'white'),abs,polar,input()
def r(s):
 a,C,l=0,0,3
 while _(a)<99:
  c=C+l*exp(1j*a);I.load()[c.real*30+150,150-c.imag*30]=0
  for p in P+[0]:
   N,E=X(C-c);n,e=X(C-p)
   if n<=N and _(E-e)<.1:l-=_(p-C);C=p
  a+=s
r(.01)
r(-.01)
I.show()

该脚本从stdin中读取点列表作为复数列表。

printf '[complex(0,0.5)]' | python2 snippet.py

在此处输入图片说明

printf '[complex(0,1), complex(1,1)]' | python2 snippet.py

在此处输入图片说明

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.