challenge 格子点の列挙

二次元平面上の格子点(X,Y座標がともに整数の点)を、原点から近い順に列挙してください。

同じ距離の点はどういう順番でも構いませんが、可能であればX軸に一番近い第一象限の点から原点を中心として反時計回りの順に列挙してください。 列挙の方法は、1行に一つの点の、X,Y座標を出力することとします。

サンプル出力

0, 0
1, 0
0, 1
-1, 0
0, -1
1, 1
-1, 1
1, -1
-1, -1
2, 0

最低でも1000件まで列挙できることを確認してください。 また「反時計回り」の条件も満たしている場合は、1000番目の頂点が何かも併せて答えてください。

このお題はかもさんの投稿を元にしています。ご協力ありがとうございました。

Perl がなかったので。力技。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
use strict;

my $PI = atan2(1, 1) * 4;
my $MAXR = int(sqrt(1000 / $PI) + sqrt(2) + 0.5);
my @res = ();

for (my $i = 0; $i <= $MAXR; $i++) {
    for (my $j = 0; $j <= $MAXR; $j++) {
        my $r = sqrt($i * $i + $j * $j);
        push(@res, [$i, $j, $r, atan2($j, $i)]);
        ($i != 0) && push(@res, [-$i, $j, $r, atan2($j, -$i)]);
        ($j != 0) && push(@res, [$i, -$j, $r, atan2(-$j, $i) + 2 * $PI]);
        ($i * $j != 0) && push(@res, [-$i, -$j, $r, atan2(-$j, -$i) + 2 * $PI]);
    }
}

foreach my $p (sort {($a->[2] <=> $b->[2]) || ($a->[3] <=> $b->[3])} @res) {
    printf("%3d, %3d\n", splice(@{$p}, 0, 2));
}

Posted feedbacks - Ruby

1000番目は[1250,-1250]かな?

[[0, 0], [1, 0], [0, 1], [-1, 0], [0, -1], [1, 1], [-1, 1], [-1, -1], ...]
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
def lattice(limit)
  ary = [[0,0]]
  i = 1
  n = 1
  while i < limit
    ary << [n,0] << [0,n] << [-n,0] << [0,-n]
    ary << [n,n] << [-n,n] << [-n,-n] << [n,-n]
    i+=8
    n+=1
  end
  ary
end

距離、反時計回り(0-360°)の順にソートするために、setOrder(x,y)中で、角度/360を小数部にセットしています。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
include Math

def gridPoint(limit)
  r = []
  n = sqrt(2*limit).to_i / 2 #--- search square size
  (-n..n).each {|x|
    (-n..n).each {|y|
      r << [x, y]
    }}
  r.sort_by {|x, y|setOrder(x, y)}
end

def setOrder(x,y)
  deg = atan2(y, x) * 180 / PI
  deg += 360 if deg < 0   #--- degree correct
  x**2 + y**2 + deg / 360 #--- distance + degree/360
end

limit = 1000
k = gridPoint(limit)
(0..limit).each {|i|
  p k[i]
}

あえて三角関数atan2を使わずに、n象限の点に評価値n.xxx(xxx=0〜1未満)を設定。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
include Math

def gridPoint(limit, r = [])
  n = sqrt(2 * limit).to_i / 2 #--- search square size
  (-n..n).each {|x|
    (-n..n).each {|y|
      r << [x, y]
    }}
  r.sort_by {|x, y|setOrder(x, y)}
end

def setOrder(x, y, r = 1)
  while (x <= 0 || y < 0) && ([x, y] != [0, 0])
    x, y = y, -x
    r += 1
  end
  (r += y / (x + y + 1.0)) / 4 + (x**2 + y**2)
end

limit = 1000
p gridPoint(limit)[limit - 1] # -> [-8, 16]

充分な数サンプリングしていることを証明していないので, そこが問題点として残っていますが.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
require 'matrix'
include Math
N = 1000
class Vector
  include Enumerable #for all?
  def x; self[0]; end
  def y; self[1]; end
  def each; size.times{|i| yield self[i]} end
  def rot(rad)
    Vector[self[0]*cos(rad)-self[1]*sin(rad), self[1]*cos(rad)+self[0]*sin(rad)]
  end
end
def gen_rot_vectors(v)
  Array.new(4){|i| v.rot(PI*i.quo(2)).map{|e| e.round}} 
end
n = 0
n += 1 while n**2*PI < N #グリッドを十分な数生成するため
Array.new(n){|x| Array.new(n){|y| Vector[x, y]}}.flatten. # 第1象限内でベクトルを生成. 
  delete_if{|v| v.y == 0 unless v.x == 0}. # 重複を避けるため原点を除くy軸上の点を削除
  sort{|a,b| a.r == b.r ? a.x <=> b.x : a.r <=> b.r}.
  map{|v| v.all?{|e| e == 0} ? v : gen_rot_vectors(v)}. #原点以外のとき回転ベクトルを生成
  flatten.map{|v| v.to_a}[0..N-1].each{|e| p e}

すみません. 先程の投稿の修正です. 
充分なグリッド数のサンプリングには, サンプリング
が最低必要なグリッド数Nを含む正方形に外接す
る円を考え, それが内接している, 正方形グリッドを
考えればいいので, 
16, 17行目は, 
n = (sqrt(2)*sqrt(N).ceil).ceil
とするべきでした. 
ceilメソッドを二度も使いたくない方には, 
n = ((2+sqrt(2))*sqrt(N)).truncate
がお勧めです. 

Index

Feed

Other

Link

Pathtraq

loading...