Ruby (135 znaków)
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}
Próbka wyjściowa
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
Awaria
To nie jest zbyt oczywiste, jak to działa, więc oto szybki podział. UWAGA: Prawdopodobnie możesz pominąć niektóre z tych kroków i szybciej przejść do krótszych wersji, ale myślę, że jest to wystarczająco edukacyjne, aby zobaczyć różne sposoby golenia znaków, szczególnie poprzez wykrywanie wzorców w literałach, aby zamieniać 2-cyfrowe liczby na 1-cyfrowe wersje .
Wersja naiwna
W przeciwieństwie do innych rozwiązań Ruby, które opierają się na dwuwymiarowej tablicy, możesz (ostatecznie) uzyskać krótszą wersję, zaczynając od tablicy jednowymiarowej i pracując z wartościami przesunięcia, ponieważ wzorce się powtarzają.
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
Kluczową zasadą jest to, że pracujemy na pozycjach indeksu 8, 10, 12, po prostu przesuniętych o wielokrotności 14. Pozycje 8, 10 i 12 są środkami sumowanych siatek 3x3. Na wyjściu próbki 34 to pozycja 8, 42 to pozycja 8 + 14 * 1 itd. Zastępujemy pozycję 8 34 pozycjami przesuniętymi od pozycji 8 o [-8,-7,-6,-1,1,6,7,8]
- innymi słowy 34 = sum(ary[8-8], ary[8-7], ..., ary[8+8])
. Ta sama zasada obowiązuje dla wszystkich wartości [8 + 14*i, 10 + 14*i, 12 + 14*i]
, ponieważ wzorzec się powtarza.
Optymalizacja
Po pierwsze, kilka szybkich optymalizacji:
- Zamiast za każdym razem „
3.times { ... }
obliczać j + 14*i
” pozycje [8,10,12,22,24,26,36,38,40]
.
offsets
Tablica jest użyta tylko raz, aby zastąpić zmienną dosłownym.
- Wymień
do ... end
się {...}
i przełączyć okolice drukowanie $> << foo
. (Jest tu sztuczka z udziałem puts nil
i () == nil
.)
- Krótsze nazwy zmiennych.
Kod po tym ma 177 znaków:
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)}
Przy następnej redukcji zwróć uwagę, że inject
tablica przesunięć nie musi być w porządku. Możemy mieć [-8,-7,-6,-1,1,6,7,8]
albo inne zamówienie, ponieważ dodawanie jest przemienne.
Więc najpierw połącz pozytywne i negatywne [1,-1,6,-6,7,-7,8,-8]
.
Teraz możesz skrócić
[1,-1,6,-6,7,-7,8,-8].map { |e| j+e }.inject(:+)
do
[1,6,7,8].flat_map { |e| [j+e, j-e] }
To skutkuje
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)}
który ma 176 znaków.
Przesuń o 8 i przejdź do różnic
Dwuznakowe wartości literalne wydają się być możliwe do skrócenia, więc weź [8,10,12,22,24,26,36,38,40]
i przenieś wszystko w dół 8
, aktualizując j
na początku pętli. (Należy pamiętać, że +=8
unika się konieczności aktualizacji wartości przesunięcia 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)}
Jest to 179, co jest większe, ale j+=8
faktycznie można je usunąć.
Pierwsza zmiana
[0,2,4,14,16,18,28,30,32]
do szeregu różnic:
[2,2,10,2,2,10,2,2]
i kumulatywnie dodaj te wartości do wartości początkowej j=8
. To ostatecznie obejmie te same wartości. (Prawdopodobnie moglibyśmy przejść bezpośrednio do tego, zamiast przejść najpierw o 8).
Zauważ, że dodamy również wartość fikcyjną 9999
na końcu tablicy różnic i dodamy j
na końcu , a nie na początku pętli. Uzasadnieniem jest to, 2,2,10,2,2,10,2,2
że okropnie zbliża się do bycia tymi samymi 3 liczbami powtarzanymi 3 razy, a obliczając j+difference
na końcu pętli, ostateczna wartość 9999
nie wpłynie na wynik, ponieważ nie ma a[j]
wywołania, w którym j
jest jakaś wartość ponad 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)}
Z tą tablicą różnic j+=8
jest teraz po prostu j=8
oczywiście, ponieważ w przeciwnym razie wielokrotnie dodawaliśmy 8
zbyt wiele. Zmieniliśmy również zmienną blokową z j
na l
.
Ponieważ 9999
element nie ma wpływu na wynik, możemy go zmienić 10
i skrócić tablicę.
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)}
To jest 170 znaków.
Ale teraz j=8
wygląda to trochę niezgrabnie i możesz uratować 2 postacie, przesuwając w [2,2,10]
dół o 2, aby wygodnie uzyskać postać, 8
której możesz użyć do przypisania. To też musi j+=l
się stać 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)}
To jest 169 znaków. Okrągły sposób wyciskania 7 znaków, ale jest fajny.
Ostatnie poprawki
values_at
Połączenie jest rzeczywiście rodzaju zbędne i możemy inline takie Array#[]
połączenie. Więc
a.values_at(*[1,6,7,8].flat_map{|e|[j+e,j-e]}).inject(:+)
staje się
[1,6,7,8].flat_map{|e|[a[j+e],a[j-e]]}.inject(:+)
Możesz również zauważyć, że flat_map
+ j+e/j-e
+ inject
można zredukować do bardziej bezpośredniego sumowania z inicjałem 0
w tablicy.
To daje ci 152 znaki:
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)}
Wreszcie:
map.with_index
może zostać each_slice
.
- Zmień podejście do drukowania.
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}