Ruby (135 caracteres)
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}
Saída de amostra
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
Demolir
Não é muito óbvio como isso funciona, então aqui está um rápido resumo. NOTA: você provavelmente pode pular algumas dessas etapas e pular para versões mais curtas mais rapidamente, mas acho que é educativo o suficiente para ver as diferentes maneiras em que raspei os caracteres, principalmente identificando padrões em literais para transformar números de 2 dígitos em versões de 1 dígito .
Versão ingênua
Diferente das outras soluções Ruby que dependem de uma matriz bidimensional, você pode (eventualmente) obter uma versão mais curta iniciando com uma matriz unidimensional e trabalhando com valores de deslocamento, uma vez que os padrões se repetem.
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
O princípio chave aqui é que estamos trabalhando nas posições 8, 10, 12 do índice, apenas compensadas por múltiplos de 14. As posições 8, 10 e 12 são os centros das grades 3x3 que estamos resumindo. Na saída de amostra, 34 é a posição 8, 42 é a posição 8 + 14 * 1, etc. Substituímos a posição 8 por 34 por posições deslocadas da posição 8 por [-8,-7,-6,-1,1,6,7,8]
- em outras palavras 34 = sum(ary[8-8], ary[8-7], ..., ary[8+8])
. Esse mesmo princípio vale para todos os valores de [8 + 14*i, 10 + 14*i, 12 + 14*i]
, uma vez que o padrão se repete.
Otimizando
Primeiro, algumas otimizações rápidas:
- Em vez de
3.times { ... }
, e calcular j + 14*i
cada vez, "inline" as posições [8,10,12,22,24,26,36,38,40]
.
- A
offsets
matriz é usada uma vez, portanto substitua a variável pelo literal.
- Substitua
do ... end
por {...}
e alterne a impressão para $> << foo
. (Há um truque aqui envolvendo puts nil
e () == nil
.)
- Nomes de variáveis mais curtos.
O código depois disso tem 177 caracteres:
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)}
Para a próxima redução, observe que inject
não é necessário que a matriz de compensações esteja em ordem. Podemos ter [-8,-7,-6,-1,1,6,7,8]
ou alguma outra ordem, pois a adição é comutativa.
Então, primeiro emparelhe os pontos positivos e negativos de obter [1,-1,6,-6,7,-7,8,-8]
.
Agora você pode encurtar
[1,-1,6,-6,7,-7,8,-8].map { |e| j+e }.inject(:+)
para
[1,6,7,8].flat_map { |e| [j+e, j-e] }
Isto resulta em
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)}
que tem 176 caracteres.
Mude para 8 e mude para diferenças
Os valores literais de dois caracteres parecem que podem ser reduzidos, então pegue [8,10,12,22,24,26,36,38,40]
e mude tudo 8
, atualizando j
no início do loop. (Observe que +=8
evita a necessidade de atualizar os valores de deslocamento de 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)}
Isso é 179, que é maior, mas j+=8
pode realmente ser removido.
Primeira mudança
[0,2,4,14,16,18,28,30,32]
para uma variedade de diferenças:
[2,2,10,2,2,10,2,2]
e adicione cumulativamente esses valores a uma inicial j=8
. Isso acabará por cobrir os mesmos valores. (Nós provavelmente poderíamos pular direto para isso, em vez de mudar para 8 primeiro.)
Note que nós também vamos adicionar um valor fictício de 9999
ao final da matriz diferenças, e adicionar j
no final , não o início do loop. A justificativa é que 2,2,10,2,2,10,2,2
parece muito próximo de ser os mesmos 3 números repetidos 3 vezes e calculandoj+difference
no final do loop, o valor final de 9999
realmente não afetará a saída, pois não há uma a[j]
chamada em que haja j
algum valor. acabou 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)}
Com esse conjunto de diferenças, o j+=8
agora é apenas j=8
, é claro, pois caso contrário, adicionaríamos repetidamente 8
muitos. Também alteramos a variável de bloco de j
para l
.
Portanto, como o 9999
elemento não tem efeito na saída, podemos alterá-lo para 10
e encurtar a matriz.
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)}
São 170 caracteres.
Mas agora a j=8
aparência é um pouco desajeitada e você pode salvar 2 caracteres deslocando [2,2,10]
a tecla 2 para baixo para obter convenientemente um que 8
você pode usar para atribuição. Isso também precisa j+=l
se tornar 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)}
São 169 caracteres. Uma maneira geral de espremer 7 caracteres, mas é legal.
Ajustes finais
A values_at
chamada é realmente redundante e podemos incorporar uma Array#[]
chamada. então
a.values_at(*[1,6,7,8].flat_map{|e|[j+e,j-e]}).inject(:+)
torna-se
[1,6,7,8].flat_map{|e|[a[j+e],a[j-e]]}.inject(:+)
Você também pode identificar que flat_map
+ j+e/j-e
+inject
pode ser reduzido a um somatório mais direto com uma inicial 0
na matriz.
Isso deixa você com 152 caracteres:
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)}
Finalmente:
map.with_index
pode se tornar each_slice
.
- Mude a abordagem de impressão.
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}