Рубин (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
Сломать
Не слишком очевидно, как это работает, так что вот краткий анализ. НОТА: Вероятно, вы можете пропустить некоторые из этих шагов и перейти к более коротким версиям, но я думаю, что это достаточно познавательно, чтобы увидеть различные способы, которыми я сбрил символы, особенно, обнаруживая шаблоны в литералах, чтобы превратить двузначные числа в однозначные версии. ,
Наивная версия
В отличие от других решений 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
массив смещений не должен быть в порядке. Мы можем иметь [-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]
и кумулятивно добавить эти значения к исходному 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,2,10]
вниз на 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}