Ruby(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位数字的方式来剃除字符。 。
天真的版本
与其他依赖于二维数组的Ruby解决方案不同,您可以(最终)通过从一维数组开始并使用偏移值来获得较短的版本,因为模式会重复。
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替换为34,将位置从位置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
不需要按顺序排列offsets数组。[-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移至不同点
两个字符的文字值似乎可以缩短,因此请在循环开始[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
在end而不是循环的开始处添加。理由是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
太多。我们还将block变量从更改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}