ルビー(135文字)
a=(0..48).map{rand(9)+1}
([0,0,j=8]*3).each{|l|a[j]=[0,1,6,7,8].inject{|s,e|s+a[j+e]+a[j-e]};j+=l+2}
a.each_slice(7){|r|puts"%-3s"*7%r}
サンプル出力
2 1 6 9 4 5 1
9 34 4 37 2 31 3
7 2 3 1 8 1 7
5 42 4 40 2 47 9
3 9 9 4 9 4 7
3 44 4 41 2 47 4
6 9 1 5 7 6 8
壊す
これがどのように機能するかはあまり明らかではないので、ここで簡単な内訳を示します。注意:これらの手順の一部をスキップして短いバージョンにすばやくジャンプすることもできますが、特にリテラルのパターンを見つけて2桁の数字を1桁のバージョンに変換することで、文字を削るさまざまな方法を見るのに十分な教育だと思います。
素朴なバージョン
2次元配列に依存する他のRubyソリューションとは異なり、パターンが繰り返されるため、1次元配列から始めてオフセット値を操作することにより、(最終的に)短いバージョンを取得できます。
ary=(0..48).map { rand(9) + 1 }
offsets = [-8,-7,-6,-1,1,6,7,8]
3.times do |i|
[8,10,12].each do |j|
ary[j + 14*i] = ary.values_at(*offsets.map { |e| j+14*i + e }).inject(:+)
end
end
ary.each.with_index do |e,i|
$> << ("%-3s" % e)
$> << ?\n if i % 7==6
end
ここでの重要な原則は、インデックス位置8、10、12で作業していることです。14の倍数だけオフセットしています。位置8、10、および12は、合計する3x3グリッドの中心です。サンプル出力では、34は位置8、42は位置8 + 14 * 1などです。位置8を位置8から[-8,-7,-6,-1,1,6,7,8]
—、つまり34 = sum(ary[8-8], ary[8-7], ..., ary[8+8])
。この同じ原則は、[8 + 14*i, 10 + 14*i, 12 + 14*i]
パターンが繰り返されるため、ます。
最適化する
まず、いくつかの簡単な最適化:
- の代わりに
3.times { ... }
、j + 14*i
毎回計算して、位置を「インライン化」し[8,10,12,22,24,26,36,38,40]
ます。
offsets
配列は、そのリテラルと変数を置き換え、一度使用されています。
- に置き換え
do ... end
て{...}
、印刷をに切り替え$> << foo
ます。(ここにとを含むトリックがputs nil
あり() == nil
ます。)
- より短い変数名。
この後のコードは177文字です。
a=(0..48).map{rand(9)+1}
[8,10,12,22,24,26,36,38,40].each{|j|a[j]=a.values_at(*[-8,-7,-6,-1,1,6,7,8].map{|e|j+e}).inject(:+)}
a.map.with_index{|e,i|$><<"%-3s"%e<<(?\nif i%7==6)}
次のリダクションでinject
は、オフセット配列を順番に並べる必要がないことに注意してください。[-8,-7,-6,-1,1,6,7,8]
加算は可換であるため、順序を付けることも、他の順序にすることもできます。
したがって、最初に正と負を組み合わせて取得し[1,-1,6,-6,7,-7,8,-8]
ます。
これで短縮できます
[1,-1,6,-6,7,-7,8,-8].map { |e| j+e }.inject(:+)
に
[1,6,7,8].flat_map { |e| [j+e, j-e] }
これにより
a=(0..48).map{rand(9)+1}
[8,10,12,22,24,26,36,38,40].each{|j|a[j]=a.values_at(*[1,6,7,8].flat_map{|e|[j+e,j-e]}).inject(:+)}
a.map.with_index{|e,i|$><<"%-3s"%e<<(?\nif i%7==6)}
これは176の文字。
8シフトして違いに移動する
2文字のリテラル値は短縮できるように見えるので、ループの開始時に更新して[8,10,12,22,24,26,36,38,40]
すべてを下にシフトします。(のオフセット値を更新する必要がなくなることに注意してください。)8
j
+=8
1,6,7,8
a=(0..48).map{rand(9)+1}
[0,2,4,14,16,18,28,30,32].each{|j|j+=8;a[j]=a.values_at(*[1,6,7,8].flat_map{|e|[j+e,j-e]}).inject(:+)}
a.map.with_index{|e,i|$><<"%-3s"%e<<(?\nif i%7==6)}
これは179です。これは大きいですが、j+=8
実際には削除できます。
最初の変更
[0,2,4,14,16,18,28,30,32]
違いの配列へ:
[2,2,10,2,2,10,2,2]
そして、これらの値を累積的にinitialに追加しますj=8
。これは最終的に同じ値をカバーします。(最初に8ずつシフトするのではなく、おそらくこれに直接スキップできます。)
我々はまたのダミーの値を追加しますことを注意9999
違い配列の末尾に、そしてに追加j
で終わりではなく、スタートループのを。正当な理由があるということ2,2,10,2,2,10,2,2
ルックスとても近い同じ3つの数字であることには、3回繰り返して、計算することによって、j+difference
ループの最後で、最終的な値が9999
存在しないので、実際に、出力には影響しませんa[j]
呼び出しj
、いくつかの値であるが、以上10000
。
a=(0..48).map{rand(9)+1}
j=8
[2,2,10,2,2,10,2,2,9999].each{|l|a[j]=a.values_at(*[1,6,7,8].flat_map{|e|[j+e,j-e]}).inject(:+);j+=l}
a.map.with_index{|e,i|$><<"%-3s"%e<<(?\nif i%7==6)}
この差分配列を使用するj+=8
とj=8
、もちろん、が8
多すぎます。また、ブロック変数をからj
に変更しましたl
。
したがって、9999
要素は出力に影響を与えないため、要素を変更して10
配列を短くすることができます。
a=(0..48).map{rand(9)+1}
j=8
([2,2,10]*3).each{|l|a[j]=a.values_at(*[1,6,7,8].flat_map{|e|[j+e,j-e]}).inject(:+);j+=l}
a.map.with_index{|e,i|$><<"%-3s"%e<<(?\nif i%7==6)}
これは170文字です。
しかし、今はj=8
少し不格好になり、割り当てに使用できるよう[2,2,10]
に便利に取得するために2ずつシフトダウンすることで2文字を節約8
できます。これもj+=l
になる必要がありますj+=l+2
。
a=(0..48).map{rand(9)+1}
([0,0,j=8]*3).each{|l|a[j]=a.values_at(*[1,6,7,8].flat_map{|e|[j+e,j-e]}).inject(:+);j+=l+2}
a.map.with_index{|e,i|$><<"%-3s"%e<<(?\nif i%7==6)}
これは169文字です。7文字を圧縮するための回り道ですが、それはすっきりしています。
最終調整
values_at
呼び出しは、実際にソート冗長であり、そして我々は、インライン化することができますArray#[]
コールを。そう
a.values_at(*[1,6,7,8].flat_map{|e|[j+e,j-e]}).inject(:+)
になる
[1,6,7,8].flat_map{|e|[a[j+e],a[j-e]]}.inject(:+)
また、配列内のイニシャルを使用して、flat_map
+ j+e/j-e
+ inject
をより直接的な合計に削減できることを確認できます0
。
これにより、152文字が残ります。
a=(0..48).map{rand(9)+1}
([0,0,j=8]*3).each{|l|a[j]=[0,1,6,7,8].inject{|s,e|s+a[j+e]+a[j-e]};j+=l+2}
a.map.with_index{|e,i|$><<"%-3s"%e<<(?\nif i%7==6)}
最後に:
map.with_index
になることができeach_slice
ます。
- 印刷方法を変更します。
135:
a=(0..48).map{rand(9)+1}
([0,0,j=8]*3).each{|l|a[j]=[0,1,6,7,8].inject{|s,e|s+a[j+e]+a[j-e]};j+=l+2}
a.each_slice(7){|r|puts"%-3s"*7%r}