単純な配列があります。配列の長さは常に整数の平方根です。したがって、16、25、36など。
$array = array('1', '2', '3', '4' ... '25');
私がやっていることは、配列をHTMLで配置して、辺が偶数のブロックのように見えるようにすることです。
私がやりたいのは、要素を並べ替えることです。これにより、JSONでエンコードされた配列をjQueryに渡すと、配列が繰り返され、現在のブロックがフェードインするので、一種のウェーブアニメーションが得られます。だから私はこのような配列をソートしたいと思います
だから私のソートされた配列は次のようになります
$sorted = array('1', '6', '2', '3', '7', '11', '16, '12' .. '25');
そうする方法はありますか?..ありがとう
これが私のものです。
function waveSort(array $array) {
$dimension = pow(count($array),0.5);
if((int)$dimension != $dimension) {
throw new InvalidArgumentException();
}
$tempArray = array();
for($i = 0; $i < $dimension; $i++) {
$tempArray[] = array_slice($array,$i*$dimension,$dimension);
}
$returnArray = array();
for($i = 0; $i < $dimension * 2 -1; $i++) {
$diagonal = array();
foreach($tempArray as $x => $innerArray) {
if($i - $x >= 0 && $i - $x < $dimension) {
$diagonal[] = $innerArray[$i - $x];
}
}
if($i % 2 == 1) {
krsort($diagonal);
}
$returnArray = array_merge($returnArray,$diagonal);
}
return $returnArray;
}
使用法:
<?php
$a = range(1,25);
var_dump(waveSort($a));
出力
array(25) {
[0]=>
int(1)
[1]=>
int(6)
[2]=>
int(2)
[3]=>
int(3)
[4]=>
int(7)
[5]=>
int(11)
[6]=>
int(16)
[7]=>
int(12)
[8]=>
int(8)
[9]=>
int(4)
[10]=>
int(5)
[11]=>
int(9)
[12]=>
int(13)
[13]=>
int(17)
[14]=>
int(21)
[15]=>
int(22)
[16]=>
int(18)
[17]=>
int(14)
[18]=>
int(10)
[19]=>
int(15)
[20]=>
int(19)
[21]=>
int(23)
[22]=>
int(24)
[23]=>
int(20)
[24]=>
int(25)
}
とてもクールな質問です。これが分析とアルゴリズムです。
このアルゴリズムを使用する主な利点は、すべてが単純な整数計算を使用して実行されることです。 「if」ステートメントがないため、ブランチもありません。つまり、コンパイルされた場合、nの値が非常に大きい場合でも非常に高速に実行されます。これは、nの値が非常に大きい場合に、作業を複数のプロセッサに分割することを簡単に並列化できることも意味します。
このジグザグパターンに従う8x8グリッド(ここでは、入力は技術的にはn = 64ですが、以下の式では簡単にするためにn = 8を使用します)を考えてみましょう(0インデックスの行と列の軸を使用)。
_ [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7]
[ 0] 1 3 4 10 11 21 22 36
[ 1] 2 5 9 12 20 23 35 37
[ 2] 6 8 13 19 24 34 38 49
[ 3] 7 14 18 25 33 39 48 50
[ 4] 15 17 26 32 40 47 51 58
[ 5] 16 27 31 41 46 52 57 59
[ 6] 28 30 42 45 53 56 60 63
[ 7] 29 43 44 54 55 61 62 64
_
最初に、左下(0,7)から右上(7,0)への対角線が、グリッドを2つのほぼミラーリングされたコンポーネントに分割していることに注意してください。
_ [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7]
[ 0] 1 3 4 10 11 21 22 36
[ 1] 2 5 9 12 20 23 35
[ 2] 6 8 13 19 24 34
[ 3] 7 14 18 25 33
[ 4] 15 17 26 32
[ 5] 16 27 31
[ 6] 28 30
[ 7] 29
_
そして
_ [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7]
[ 0] 36
[ 1] 35 37
[ 2] 34 38 49
[ 3] 33 39 48 50
[ 4] 32 40 47 51 58
[ 5] 31 41 46 52 57 59
[ 6] 30 42 45 53 56 60 63
[ 7] 29 43 44 54 55 61 62 64
_
右下がちょうど左上にミラーリングされ、正方形プラス1(この場合は65)から差し引かれていることがわかります。
左上部分を計算できれば、二乗プラス1(_n * n + 1
_)を取り、逆座標の値を引く(value(n - x - 1, n - y - 1)
)だけで、右下部分を簡単に計算できます。 )。
例として、右下の部分にある任意の座標のペア、たとえば(6,3)で、値が48であるとします。この式に従うと、_(8 * 8 + 1) - value(8 - 6 - 1, 8 - 3 - 1)
_になり、65 - value(1, 4)
。左上の部分を見ると、(1,4)の値は17です。そして_65 - 17 == 48
_。
ただし、左上の部分を計算する必要があります。これは、2つの重なり合うコンポーネントにさらに細分化することもできることに注意してください。1つのコンポーネントは、直立するにつれて番号が増加します。
_ [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7]
[ 0] 3 10 21 36
[ 1] 2 9 20 35
[ 2] 8 19 34
[ 3] 7 18 33
[ 4] 17 32
[ 5] 16 31
[ 6] 30
[ 7] 29
_
そして、左下に向かうにつれて数字が増加する1つのコンポーネント:
_ [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7]
[ 0] 1 4 11 22
[ 1] 5 12 23
[ 2] 6 13 24
[ 3] 14 25
[ 4] 15 26
[ 5] 27
[ 6] 28
[ 7]
_
前者は、座標の合計(_x + y
_)が奇数である数として定義することもでき、後者は、座標の合計が偶数である数として定義することもできます。
さて、ここでの重要な洞察は、ここで三角形を描いているということです。したがって、驚くことではありませんが、 三角数 がここで重要な役割を果たします。三角数のシーケンスは次のとおりです:1、3、6、10、15、21、28、36、.。
ご覧のとおり、奇数合計コンポーネントでは、3で始まる1つおきの三角数が最初の行(3、10、21、36)に表示され、偶数合計コンポーネントでは、1で始まる1つおきの三角数が表示されます。最初の列(1、6、15、28)。
具体的には、特定の座標ペア(x、0)または(0、y)の場合、対応する三角数はtriangle(x + 1)またはtriangle(y + 1)です。
また、グラフの残りの部分は、これらの三角数から対角線の上下に増分減算することで計算できます。これは、指定された行または列の数値を減算することと同じです。
diagonalは、指定された座標の合計を持つすべてのセルのセットとして正式に定義できることに注意してください。たとえば、座標の合計が3の対角線には、座標(0,3)、(1,2)、(2,1)、および(3,0)があります。したがって、単一の数値が各対角線を定義し、その数値は開始三角数を決定するためにも使用されます。
したがって、簡単な検査から、奇数合計成分を計算する式は単純です。
_triangle(x + y + 1) - y
_
そして、偶数和成分を計算する式は単純です:
_triangle(x + y + 1) - x
_
また、三角数のよく知られた式も単純です。
_triangle(n) = (n * (n + 1)) / 2
_
したがって、アルゴリズムは次のとおりです。
value[x, y] = triangle(x + y + 1) - x
。value[x, y] = triangle(x + y + 1) - y
。n * n + 1
_から単純に減算して、右下部分のインデックスを計算します。これは、2つのネストされたループを逆方向にカウントすることで実行できます(そして、右下の部分のみを取得するために、内側のループを外側のループにバインドします)。 value[x, y] = (n * n + 1) - value[n - x - 1, n - y - 1]
。単一のループを使用し、サイメトリを利用し、ソートなしで:
function waveSort(array $array) {
$n2=count($array);
$n=sqrt($n2);
if((int)$n != $n) throw new InvalidArgumentException();
$x=0; $y=0; $dir = -1;
$Result = array_fill(0, $n2, null);
for ($i=0; $i < $n2/2; $i++) {
$p=$y * $n +$x;
$Result[$i]=$array[$p];
$Result[$n2-1-$i]=$array[$n2-1-$p];
if (($dir==1)&&($y==0)) {
$x++;
$dir *= -1;
} else if (($dir==-1)&&($x==0)) {
$y++;
$dir *= -1;
} else {
$x += $dir;
$y -= $dir;
}
}
return $Result;
}
$a = range(1,25);
var_dump(waveSort($a));
この質問にはすでに多くの解決策がありますが、これは私のものです。
他のソリューションとの違いの主な機能:
起源:
<?php
function zigzag($input)
{
$output = array();
$inc = -1;
$i = $j = 0;
$steps = 0;
$bounds = sqrt(sizeof($input));
if(fmod($bounds, 1) != 0)
{
die('Matrix must be square');
}
while($steps < sizeof($input))
{
if($i >= $bounds) // bottom Edge
{
$i--;
$j++;
$j++;
$inc = 1;
}
if($j >= $bounds) // right Edge
{
$i++;
$i++;
$j--;
$inc = -1;
}
if($j < 0) // left Edge
{
$j++;
$inc = 1;
}
if($i < 0) // top Edge
{
$i++;
$inc = -1;
}
$output[] = $input[$bounds * $i + $j];
$i = $i - $inc;
$j = $j + $inc;
$steps++;
}
return $output;
}
$a = range(1,25);
var_dump(zigzag($a));
ちなみに、この種のアルゴリズムは「ジグザグスキャン」と呼ばれ、JPEGやMPEGコーディングに多用されています。
これは私の見解です。 qiuntusの応答に似ていますが、より簡潔です。
function wave($base) {
$i = 1;
$a = $base;
$square = $base*$base;
$out = array(1);
while ($i < $square) {
if ($i > ($square - $base)) { // hit the bottom
$i++;
$out[] = $i;
$a = 1 - $base;
} elseif ($i % $base == 0) { // hit the right
$i += $base;
$out[] = $i;
$a = $base - 1;
} elseif (($i - 1) % $base == 0) { // hit the left
$i += $base;
$out[] = $i;
$a = 1 - $base;
} elseif ($i <= $base) { // hit the top
$i++;
$out[] = $i;
$a = $base - 1;
}
if ($i < $square) {
$i += $a;
$out[] = $i;
}
}
return $out;
}
もう1つのPHPソリューション、for
とif
のみを使用して、配列を1回だけトラバースします
function waveSort(array $array) {
$elem = sqrt(count($array));
for($i = 0; $i < $elem; $i++) {
$multi[] = array_slice($array, $i*$elem , $elem);
}
$new = array();
$rotation = false;
for($i = 0; $i <= $elem-1; $i++) {
$k = $i;
for($j = 0; $j <= $i; $j++) {
if($rotation)
$new[] = $multi[$k][$j];
else
$new[] = $multi[$j][$k];
$k--;
}
$rotation = !$rotation;
}
for($i = $elem-1; $i > 0; $i--) {
$k = $elem - $i;
for($j = $elem-1; $j >= $elem - $i; $j--) {
if(!$rotation)
$new[] = $multi[$k][$j];
else
$new[] = $multi[$j][$k];
$k++;
}
$rotation = !$rotation;
}
return $new;
}
$array = range(1, 25);
$result = waveSort($array);
print_r($result);
$array = range(1, 36);
$result = waveSort($array);
print_r($result);
私はそれをC#で書いたので、PHPでコンパイル/解析しませんでしたが、このロジックは機能するはずです:
List<long> newList = new List<long>();
double i = 1;
double root = Math.Sqrt(oldList.Count);
bool direction = true;
while (newList.Count < oldList.Count)
{
newList.Add(oldList[(int)i - 1]);
if (direction)
{
if (i + root > root * root)
{
i++;
direction = false;
}
else if (i % root == 1)
{
i += 5;
direction = false;
}
else
{
i += root - 1;
}
}
else
{
if (i - root <= 0)
{
direction = true;
if (i % root == 0)
{
i += root;
}
else
{
i++;
}
direction = true;
}
else if (i % root == 0)
{
direction = true;
i += root;
}
else
{
i += 1 - root;
}
}
}
PHPバージョンは次のようになります:
$oldList = ...
$newList = [];
$i = 1;
$root = sqrt(Count($oldList);
$direction = true;
while (count($newList) < count($oldList)
{
$newList[] = $oldList[$i - 1];
if ($direction)
{
if ($i + $root > $root * $root)
{
$i++;
$direction = false;
}
else if ($i % $root == 1)
{
$i += 5;
$direction = false;
}
else
{
$i += $root - 1;
}
}
else
{
if ($i - $root <= 0)
{
$direction = true;
if ($i % $root == 0)
{
$i += $root;
}
else
{
i++;
}
direction = true;
}
else if ($i % $root == 0)
{
$direction = true;
$i += $root;
}
else
{
$i += 1 - $root;
}
}
}
例Python実装:
def wave(size):
curX = 0
curY = 0
direction = "down"
positions = []
positions.append((curX, curY))
while not (curX == size-1 and curY == size-1):
if direction == "down":
if curY == size-1: #can't move down any more; move right instead
curX += 1
else:
curY += 1
positions.append((curX, curY))
#move diagonally up and right
while curX < size-1 and curY > 0:
curX += 1
curY -= 1
positions.append((curX, curY))
direction = "right"
continue
else: #direction == "right"
if curX == size-1: #can't move right any more; move down instead
curY += 1
else:
curX += 1
positions.append((curX, curY))
#move diagonally down and left
while curY < size-1 and curX > 0:
curX -= 1
curY += 1
positions.append((curX, curY))
direction = "down"
continue
return positions
size = 5
for x, y in wave(size):
index = 1 + x + (y*size)
print index, x, y
出力:
1 0 0
6 0 1
2 1 0
3 2 0
7 1 1
11 0 2
16 0 3
12 1 2
8 2 1
4 3 0
5 4 0
9 3 1
13 2 2
17 1 3
21 0 4
22 1 4
18 2 3
14 3 2
10 4 1
15 4 2
19 3 3
23 2 4
24 3 4
20 4 3
25 4 4
コメディ1行の実装:
def wave(size):
return [1+x+size*y for x,y in filter(lambda (x,y): x >=0 and x < size and y >= 0 and y < size, reduce(lambda x, y: x+y, [r if i==0 else list(reversed(r)) for i, r in enumerate([(x-delta, delta) for delta in range(size)] for x in range(size*2))], []))]
print wave(5)
出力:
[1, 6, 2, 11, 7, 3, 16, 12, 8, 4, 21, 17, 13, 9, 5, 22, 18, 14, 10, 23, 19, 15, 24, 20, 25]