例如,这里是预期螺旋的形状(以及迭代的每个步骤)
y
|
|
16 15 14 13 12
17 4 3 2 11
-- 18 5 0 1 10 --- x
19 6 7 8 9
20 21 22 23 24
|
|
线条是x和y轴。
这将是算法在每次迭代时“返回”的实际值(点的坐标):
[0,0],
[1,0], [1,1], [0,1], [-1,1], [-1,0], [-1,-1], [0,-1], [1,-1],
[2,-1], [2,0], [2,1], [2,2], [1,2], [0,2], [-1,2], [-2,2], [-2,1], [-2,0]..
等
我已经尝试过搜索,但我并不确定该搜索到底是什么,以及我尝试过哪些搜索都会产生死角。
我甚至不确定从哪里开始,除了凌乱,不雅和特别的东西,比如为每一层创建/编码新的螺旋。
任何人都可以帮助我开始吗?
另外,有没有一种方法可以轻松地在顺时针和逆时针(方向)之间切换,以及从哪个方向“开始”螺旋? (轮换)
另外,有没有办法以递归方式执行此操作?
我的申请
我有一个填充了数据点的稀疏网格,我想在网格中添加一个新的数据点,并让它“尽可能接近”给定的其他点。
为此,我将调用grid.find_closest_available_point_to(point)
,它将迭代上面给出的螺旋并返回第一个空位且可用的位置。
首先,它会检查point+[0,0]
(只是为了完整性)。然后它会检查point+[1,0]
。然后它会检查point+[1,1]
。然后point+[0,1]
等等。返回网格中的位置为空(或者没有被数据点占用)的第一个。
网格大小没有上限。
答案 0 :(得分:21)
直接的“ad-hoc”解决方案没有任何问题。它也足够干净。
请注意,螺旋是由段构建的。你可以从当前的一个旋转90度的下一段。并且每两个旋转,段的长度增加1。
修改插图,编号为
的段 ... 11 10
7 7 7 7 6 10
8 3 3 2 6 10
8 4 . 1 6 10
8 4 5 5 5 10
8 9 9 9 9 9
// (di, dj) is a vector - direction in which we move right now
int di = 1;
int dj = 0;
// length of current segment
int segment_length = 1;
// current position (i, j) and how much of current segment we passed
int i = 0;
int j = 0;
int segment_passed = 0;
for (int k = 0; k < NUMBER_OF_POINTS; ++k) {
// make a step, add 'direction' vector (di, dj) to current position (i, j)
i += di;
j += dj;
++segment_passed;
System.out.println(i + " " + j);
if (segment_passed == segment_length) {
// done with current segment
segment_passed = 0;
// 'rotate' directions
int buffer = di;
di = -dj;
dj = buffer;
// increase segment length if necessary
if (dj == 0) {
++segment_length;
}
}
}
要更改原始方向,请查看di
和dj
的原始值。要将旋转切换为顺时针,请查看这些值的修改方式。
答案 1 :(得分:14)
这是C ++中的一个有状态迭代器。
class SpiralOut{
protected:
unsigned layer;
unsigned leg;
public:
int x, y; //read these as output from next, do not modify.
SpiralOut():layer(1),leg(0),x(0),y(0){}
void goNext(){
switch(leg){
case 0: ++x; if(x == layer) ++leg; break;
case 1: ++y; if(y == layer) ++leg; break;
case 2: --x; if(-x == layer) ++leg; break;
case 3: --y; if(-y == layer){ leg = 0; ++layer; } break;
}
}
};
应该尽可能高效。
答案 2 :(得分:10)
这是基于答案的javascript解决方案 Looping in a spiral
var x = 0,
y = 0,
delta = [0, -1],
// spiral width
width = 6,
// spiral height
height = 6;
for (i = Math.pow(Math.max(width, height), 2); i>0; i--) {
if ((-width/2 < x && x <= width/2)
&& (-height/2 < y && y <= height/2)) {
console.debug('POINT', x, y);
}
if (x === y
|| (x < 0 && x === -y)
|| (x > 0 && x === 1-y)){
// change direction
delta = [-delta[1], delta[0]]
}
x += delta[0];
y += delta[1];
}
答案 3 :(得分:7)
通过分析如何改变螺旋角的坐标可以最好地理解这个问题。考虑这个前8个螺旋角(不包括原点)的表:
x,y | dx,dy | k-th corner | N | Sign | ___________________________________________ 1,0 | 1,0 | 1 | 1 | + 1,1 | 0,1 | 2 | 1 | + -1,1 | -2,0 | 3 | 2 | - -1,-1 | 0,-2 | 4 | 2 | - 2,-1 | 3,0 | 5 | 3 | + 2,2 | 0,3 | 6 | 3 | + -2,2 | -4,0 | 7 | 4 | - -2,-2 | 0,-4 | 8 | 4 | -
通过查看此表,我们可以计算给定(k-1)角的X,Y的第k个角的X,Y:
N = INT((1+k)/2) Sign = | +1 when N is Odd | -1 when N is Even [dx,dy] = | [N*Sign,0] when k is Odd | [0,N*Sign] when k is Even [X(k),Y(k)] = [X(k-1)+dx,Y(k-1)+dy]
现在,当您知道k和k + 1螺旋角的坐标时,您可以通过简单地将1或-1添加到最后一个点的x或y来获得k和k + 1之间的所有数据点。 多数民众赞成。
祝你好运。答案 4 :(得分:5)
我会用一些数学解决它。这是Ruby代码(带输入和输出):
(0..($*.pop.to_i)).each do |i|
j = Math.sqrt(i).round
k = (j ** 2 - i).abs - j
p = [k, -k].map {|l| (l + j ** 2 - i - (j % 2)) * 0.5 * (-1) ** j}.map(&:to_i)
puts "p => #{p[0]}, #{p[1]}"
end
E.g。
$ ruby spiral.rb 10
p => 0, 0
p => 1, 0
p => 1, 1
p => 0, 1
p => -1, 1
p => -1, 0
p => -1, -1
p => 0, -1
p => 1, -1
p => 2, -1
p => 2, 0
高尔夫球版:
p (0..$*.pop.to_i).map{|i|j=Math.sqrt(i).round;k=(j**2-i).abs-j;[k,-k].map{|l|(l+j**2-i-j%2)*0.5*(-1)**j}.map(&:to_i)}
修改强>
首先尝试在功能上解决问题。在每一步,你需要知道什么才能进入下一步?
专注于飞机的第一个对角线x = y
。 k
告诉您在触摸前必须采取多少步骤:负值表示您必须垂直移动abs(k)
个步骤,而正值表示您必须水平移动k
个步骤。
现在关注您当前所在线段的长度(螺旋线的顶点 - 当线段的倾斜度发生变化时 - 被视为“下一个”线段的一部分)。第一次为0
,接下来两段({2分)为1
,接下来两段为2
(= 4分),等等。细分和每次细分的部分数量增加。这就是j
的用途。
无意中,这可用于获取其他信息:(-1)**j
只是“1
的简写,如果您正在减少一些坐标以进入此步骤; -1
如果你正在增加“(注意每一步只改变一个坐标)。同样适用于j%2
,在这种情况下只需将1
替换为0
,将-1
替换为1
。这意味着它们在两个值之间交换:一个用于向上或向右“向前”的段,另一个用于向下或向左移动的段。
这是一个熟悉的推理,如果你习惯于函数式编程:其余的只是一点点简单的数学。
答案 5 :(得分:0)
尝试搜索参数或极坐标方程。两者都适合绘制螺旋形物。 Here's a page有很多例子,有图片(和方程式)。它应该给你更多的想法。
答案 6 :(得分:0)
我做的训练非常相同,输出和螺旋方向存在一些差异,并且需要额外的要求,即空间复杂度必须为O(1)。
经过一段时间的思考,我开始认识到,通过知道螺旋的起点和我计算值的位置,我可以通过减去螺旋的所有完整“圆”来简化问题,然后只计算一个更简单的值。
以下是我在ruby中实现的算法:
def print_spiral(n)
(0...n).each do |y|
(0...n).each do |x|
printf("%02d ", get_value(x, y, n))
end
print "\n"
end
end
def distance_to_border(x, y, n)
[x, y, n - 1 - x, n - 1 - y].min
end
def get_value(x, y, n)
dist = distance_to_border(x, y, n)
initial = n * n - 1
(0...dist).each do |i|
initial -= 2 * (n - 2 * i) + 2 * (n - 2 * i - 2)
end
x -= dist
y -= dist
n -= dist * 2
if y == 0 then
initial - x # If we are in the upper row
elsif y == n - 1 then
initial - n - (n - 2) - ((n - 1) - x) # If we are in the lower row
elsif x == n - 1 then
initial - n - y + 1# If we are in the right column
else
initial - 2 * n - (n - 2) - ((n - 1) - y - 1) # If we are in the left column
end
end
print_spiral 5
这不是你要求的,但我相信它会帮助你思考你的问题
答案 7 :(得分:0)
我有一个类似的问题,但我不想每次循环整个螺旋以找到下一个新坐标。要求是你知道你的最后一个坐标。
以下是我提出的关于其他解决方案的大量内容:
function getNextCoord(coord) {
// required info
var x = coord.x,
y = coord.y,
level = Math.max(Math.abs(x), Math.abs(y));
delta = {x:0, y:0};
// calculate current direction (start up)
if (-x === level)
delta.y = 1; // going up
else if (y === level)
delta.x = 1; // going right
else if (x === level)
delta.y = -1; // going down
else if (-y === level)
delta.x = -1; // going left
// check if we need to turn down or left
if (x > 0 && (x === y || x === -y)) {
// change direction (clockwise)
delta = {x: delta.y,
y: -delta.x};
}
// move to next coordinate
x += delta.x;
y += delta.y;
return {x: x,
y: y};
}
coord = {x: 0, y: 0}
for (i = 0; i < 40; i++) {
console.log('['+ coord.x +', ' + coord.y + ']');
coord = getNextCoord(coord);
}
仍然不确定它是否是最优雅的解决方案。也许一些优雅的数学可能会删除一些if语句。一些限制是需要一些修改来改变螺旋方向,不考虑非方形螺旋并且不能围绕固定坐标螺旋。
答案 8 :(得分:0)
这是算法。它顺时针旋转,但可以很容易地逆时针旋转,只需进行一些改动。我在不到一个小时的时间内完成了它。
// spiral_get_value(x,y);
sx = argument0;
sy = argument1;
a = max(sqrt(sqr(sx)),sqrt(sqr(sy)));
c = -b;
d = (b*2)+1;
us = (sy==c and sx !=c);
rs = (sx==b and sy !=c);
bs = (sy==b and sx !=b);
ls = (sx==c and sy !=b);
ra = rs*((b)*2);
ba = bs*((b)*4);
la = ls*((b)*6);
ax = (us*sx)+(bs*-sx);
ay = (rs*sy)+(ls*-sy);
add = ra+ba+la+ax+ay;
value = add+sqr(d-2)+b;
return(value);`
它将处理任何x / y值(无限)。
它是用GML(游戏制作者语言)编写的,但实际逻辑在任何编程语言中都是合理的。
单行算法只有x和y输入的2个变量(sx和sy)。我基本上扩展括号,很多。它使您可以更轻松地将其粘贴到记事本中,并将x参数/变量名称的'sx'和y参数/变量名称的'sy'更改。
`// spiral_get_value(x,y);
sx = argument0;
sy = argument1;
value = ((((sx==max(sqrt(sqr(sx)),sqrt(sqr(sy))) and sy !=(-1*max(sqrt(sqr(sx)),sqrt(sqr(sy))))))*((max(sqrt(sqr(sx)),sqrt(sqr(sy))))*2))+(((sy==max(sqrt(sqr(sx)),sqrt(sqr(sy))) and sx !=max(sqrt(sqr(sx)),sqrt(sqr(sy)))))*((max(sqrt(sqr(sx)),sqrt(sqr(sy))))*4))+(((sx==(-1*max(sqrt(sqr(sx)),sqrt(sqr(sy)))) and sy !=max(sqrt(sqr(sx)),sqrt(sqr(sy)))))*((max(sqrt(sqr(sx)),sqrt(sqr(sy))))*6))+((((sy==(-1*max(sqrt(sqr(sx)),sqrt(sqr(sy)))) and sx !=(-1*max(sqrt(sqr(sx)),sqrt(sqr(sy))))))*sx)+(((sy==max(sqrt(sqr(sx)),sqrt(sqr(sy))) and sx !=max(sqrt(sqr(sx)),sqrt(sqr(sy)))))*-sx))+(((sx==max(sqrt(sqr(sx)),sqrt(sqr(sy))) and sy !=(-1*max(sqrt(sqr(sx)),sqrt(sqr(sy))))))*sy)+(((sx==(-1*max(sqrt(sqr(sx)),sqrt(sqr(sy)))) and sy !=max(sqrt(sqr(sx)),sqrt(sqr(sy)))))*-sy))+sqr(((max(sqrt(sqr(sx)),sqrt(sqr(sy)))*2)+1)-2)+max(sqrt(sqr(sx)),sqrt(sqr(sy)));
return(value);`
我知道答复非常晚:D但我希望它能帮助未来的访客。
答案 9 :(得分:0)
我在java中有一个输出类似输出的算法,除了它优先考虑右边的数字,然后是左边的数字。
public static String[] rationals(int amount){
String[] numberList=new String[amount];
int currentNumberLeft=0;
int newNumberLeft=0;
int currentNumberRight=0;
int newNumberRight=0;
int state=1;
numberList[0]="("+newNumberLeft+","+newNumberRight+")";
boolean direction=false;
for(int count=1;count<amount;count++){
if(direction==true&&newNumberLeft==state){direction=false;state=(state<=0?(-state)+1:-state);}
else if(direction==false&&newNumberRight==state){direction=true;}
if(direction){newNumberLeft=currentNumberLeft+sign(state);}else{newNumberRight=currentNumberRight+sign(state);}
currentNumberLeft=newNumberLeft;
currentNumberRight=newNumberRight;
numberList[count]="("+newNumberLeft+","+newNumberRight+")";
}
return numberList;
}
答案 10 :(得分:0)
可以使用递归以一种非常直接的方式完成。我们只需要一些基本的2D矢量数学和工具即可生成并映射(可能是无限的)序列:
// 2D vectors
const add = ([x0, y0]) => ([x1, y1]) => [x0 + x1, y0 + y1];
const rotate = θ => ([x, y]) => [
Math.round(x * Math.cos(θ) - y * Math.sin(θ)),
Math.round(x * Math.sin(θ) + y * Math.cos(θ))
];
// Iterables
const fromGen = g => ({ [Symbol.iterator]: g });
const range = n => [...Array(n).keys()];
const map = f => it =>
fromGen(function*() {
for (const v of it) {
yield f(v);
}
});
现在我们可以通过生成一条扁平线,加上一条旋转的(扁平线,再加上一条旋转的(扁平线,再加上一条旋转的...))来递归地表示螺旋:
const spiralOut = i => {
const n = Math.floor(i / 2) + 1;
const leg = range(n).map(x => [x, 0]);
const transform = p => add([n, 0])(rotate(Math.PI / 2)(p));
return fromGen(function*() {
yield* leg;
yield* map(transform)(spiralOut(i + 1));
});
};
这将生成您感兴趣的坐标的无限列表。这是内容的示例:
const take = n => it =>
fromGen(function*() {
for (let v of it) {
if (--n < 0) break;
yield v;
}
});
const points = [...take(5)(spiralOut(0))];
console.log(points);
// => [[0,0],[1,0],[1,1],[0,1],[-1,1]]
您还可以否定旋转角度以朝另一个方向移动,或者使用变换和腿长来获得更复杂的形状。
例如,相同的技术也适用于向内螺旋。这只是一个稍有不同的变换,而用于更改腿长的方案也略有不同:
const empty = [];
const append = it1 => it2 =>
fromGen(function*() {
yield* it1;
yield* it2;
});
const spiralIn = ([w, h]) => {
const leg = range(w).map(x => [x, 0]);
const transform = p => add([w - 1, 1])(rotate(Math.PI / 2)(p));
return w * h === 0
? empty
: append(leg)(
fromGen(function*() {
yield* map(transform)(spiralIn([h - 1, w]));
})
);
};
哪个产生(这个螺旋是有限的,所以我们不需要take
一些任意数):
const points = [...spiralIn([3, 3])];
console.log(points);
// => [[0,0],[1,0],[2,0],[2,1],[2,2],[1,2],[0,2],[0,1],[1,1]]
如果想玩的话,这里有完整的实时片段:
// 2D vectors
const add = ([x0, y0]) => ([x1, y1]) => [x0 + x1, y0 + y1];
const rotate = θ => ([x, y]) => [
Math.round(x * Math.cos(θ) - y * Math.sin(θ)),
Math.round(x * Math.sin(θ) + y * Math.cos(θ))
];
// Iterables
const fromGen = g => ({ [Symbol.iterator]: g });
const range = n => [...Array(n).keys()];
const map = f => it =>
fromGen(function*() {
for (const v of it) {
yield f(v);
}
});
const take = n => it =>
fromGen(function*() {
for (let v of it) {
if (--n < 0) break;
yield v;
}
});
const empty = [];
const append = it1 => it2 =>
fromGen(function*() {
yield* it1;
yield* it2;
});
// Outward spiral
const spiralOut = i => {
const n = Math.floor(i / 2) + 1;
const leg = range(n).map(x => [x, 0]);
const transform = p => add([n, 0])(rotate(Math.PI / 2)(p));
return fromGen(function*() {
yield* leg;
yield* map(transform)(spiralOut(i + 1));
});
};
// Test
{
const points = [...take(5)(spiralOut(0))];
console.log(JSON.stringify(points));
}
// Inward spiral
const spiralIn = ([w, h]) => {
const leg = range(w).map(x => [x, 0]);
const transform = p => add([w - 1, 1])(rotate(Math.PI / 2)(p));
return w * h === 0
? empty
: append(leg)(
fromGen(function*() {
yield* map(transform)(spiralIn([h - 1, w]));
})
);
};
// Test
{
const points = [...spiralIn([3, 3])];
console.log(JSON.stringify(points));
}
答案 11 :(得分:0)
这是一个基于@mako答案的Python实现。
def spiral_iterator(iteration_limit=999):
x = 0
y = 0
layer = 1
leg = 0
iteration = 0
yield 0, 0
while iteration < iteration_limit:
iteration += 1
if leg == 0:
x += 1
if (x == layer):
leg += 1
elif leg == 1:
y += 1
if (y == layer):
leg += 1
elif leg == 2:
x -= 1
if -x == layer:
leg += 1
elif leg == 3:
y -= 1
if -y == layer:
leg = 0
layer += 1
yield x, y
运行此代码:
for x, y in spiral_iterator(10):
print(x, y)
收益:
0 0
1 0
1 1
0 1
-1 1
-1 0
-1 -1
0 -1
1 -1
2 -1
2 0