Lösa en matematisk kluring via PHP/något annat språk?

Permalänk
Medlem

Lösa en matematisk kluring via PHP/något annat språk?

Hej
Det var ett tag sedan jag fick såg denna kluring, men den har etsat sig fast i mitt huvud iaf och igår bestämde jag mig för att lösa den med programmering.
Det hela går ut på att man har siffrorna 1-9 och man skall sedan ordna dessa i en kvadrat så att dem sitter med max 3 i bredd. Sedan ska det bli samma summa både horisontellt och vertikalt, men även diagonalt.

Min lösning var att lägga 1-9 i en vektor och sedan slumpa dessa värden i vektorn som ligger i en for-loop med en körning på 987654321 rundor. Sedan är det en if-sats som kollar så att varje rad blir 15, om den hittar en som stämmer så kör den en break på loopen och printar resultatet.
Jag vet sen tidigare att 15 är summan man letar efter så därför finns 15 med som summa, men det kanske går att räkna ut vilken som är summan man letar efter?

Här är koden:

<?php //Denna kod tar siffrorna 1-9 och sätter dem i korrekt ordning för att få samma summa vertikal, horisontalt och diagonalt. //Det fungerar så att den blandar värdena i en vektor som går från 1-9 och sedan testar den sig fram tills den hittar en godkänd sträng. $time_start = microtime(true); $num=array(1,2,3,4,5,6,7,8,9); $tot=15; for($i=0;$i<987654321;$i++){ $s=$num; shuffle($s); if(($s[0]+$s[1]+$s[2])==15 && ($s[3]+$s[4]+$s[5])==15 && ($s[6]+$s[7]+$s[8])==15 && ($s[0]+$s[3]+$s[6])==15 && ($s[1]+$s[4]+$s[7])==15 && ($s[2]+$s[5]+$s[8])==15 && ($s[0]+$s[4]+$s[8])==15 && ($s[2]+$s[4]+$s[6])==15){ echo $s[0]."-".$s[1]."-".$s[2]."<br>"; echo $s[3]."-".$s[4]."-".$s[5]."<br>"; echo $s[6]."-".$s[7]."-".$s[8]."<br><br>"; break; } } $time_end=microtime(true); $time=$time_end-$time_start; echo "Found solution after $i tries and it took $time seconds\n"; ?>

För att ta reda på mittennumret så bör man ju kunna (1+2+3+4+5+6+7+8+9)/9=5

Kom med egna lösningar, det måste gå att räkna ut, inte bara slumpa fram som jag.

MVH Niclas

Permalänk
Medlem

hmm, det står ju på wikipedia

Visa signatur

I'm Winston Wolfe. I solve problems.

Permalänk
Medlem

Det kanske det gör, men vad heter denna kluring?

Permalänk
Permalänk
Medlem

Kunde inte hålla mig utan gjorde en lösning i (t)sql som tar fram alla lösningar för 3x3:

select number as n into #t from master..spt_values where type = 'P' and number between 1 and 9 select x11.n, x12.n, x13.n, x21.n, x22.n, x23.n, x31.n, x32.n, x33.n, (x11.n + x12.n + x13.n) as [sum] from #t as x11 join #t as x12 on x12.n not in (x11.n) join #t as x13 on x13.n not in (x11.n, x12.n) join #t as x21 on x21.n not in (x11.n, x12.n, x13.n) join #t as x22 on x22.n not in (x11.n, x12.n, x13.n, x21.n) join #t as x23 on x23.n not in (x11.n, x12.n, x13.n, x21.n, x22.n) join #t as x31 on x31.n not in (x11.n, x12.n, x13.n, x21.n, x22.n, x23.n) join #t as x32 on x32.n not in (x11.n, x12.n, x13.n, x21.n, x22.n, x23.n, x31.n) join #t as x33 on x33.n not in (x11.n, x12.n, x13.n, x21.n, x22.n, x23.n, x31.n, x32.n) where x11.n + x12.n + x13.n = x21.n + x22.n + x23.n and x21.n + x22.n + x23.n = x31.n + x32.n + x33.n and x31.n + x32.n + x33.n = x11.n + x21.n + x31.n and x11.n + x21.n + x31.n = x12.n + x22.n + x32.n and x12.n + x22.n + x32.n = x13.n + x23.n + x33.n and x13.n + x23.n + x33.n = x11.n + x22.n + x33.n and x11.n + x22.n + x33.n = x13.n + x22.n + x31.n drop table #t

Permalänk

Jag kunde inte heller hålla mig. Gjorde en variant i python som också tar fram alla lösningar för 3x3:

magicsquare.py

from __future__ import print_function import itertools numbers = list(range(1,10)) for x in itertools.permutations(numbers): if (x[0]+x[1]+x[2]==15 and x[3]+x[4]+x[5]==15 and x[6]+x[7]+x[8]==15 and x[0]+x[3]+x[6]==15 and x[1]+x[4]+x[7]==15 and x[2]+x[5]+x[8]== 15 and x[0]+x[4]+x[8]==15 and x[2]+x[4]+x[6]==15): print(x[0], x[1], x[2]) print(x[3], x[4], x[5]) print(x[6], x[7], x[8]) print()

Ger följande:

~ $ time python magicsquare.py 2 7 6 9 5 1 4 3 8 2 9 4 7 5 3 6 1 8 4 3 8 9 5 1 2 7 6 4 9 2 3 5 7 8 1 6 6 1 8 7 5 3 2 9 4 6 7 2 1 5 9 8 3 4 8 1 6 3 5 7 4 9 2 8 3 4 1 5 9 6 7 2 real 0m0.209s user 0m0.200s sys 0m0.010s

Permalänk
Medlem

Sjukt snyggt i python!

Visa signatur

Varför får inte rika bli rikare? Varför är det så farligt att va rasist? Vad är vitsen med att ha minst en särskrivning per mening? Hur kan man med att ge sig själv ett oseriöst intryck genom att skriva dåligt? 100% av alla mac-ägare kan inte ha fel.

Permalänk
Medlem
Skrivet av Tesseract:

Jag kunde inte heller hålla mig. Gjorde en variant i python som också tar fram alla lösningar för 3x3:

magicsquare.py

from __future__ import print_function import itertools numbers = list(range(1,10)) for x in itertools.permutations(numbers): if (x[0]+x[1]+x[2]==15 and x[3]+x[4]+x[5]==15 and x[6]+x[7]+x[8]==15 and x[0]+x[3]+x[6]==15 and x[1]+x[4]+x[7]==15 and x[2]+x[5]+x[8]== 15 and x[0]+x[4]+x[8]==15 and x[2]+x[4]+x[6]==15): print(x[0], x[1], x[2]) print(x[3], x[4], x[5]) print(x[6], x[7], x[8]) print()

Ger följande:

~ $ time python magicsquare.py 2 7 6 9 5 1 4 3 8 2 9 4 7 5 3 6 1 8 4 3 8 9 5 1 2 7 6 4 9 2 3 5 7 8 1 6 6 1 8 7 5 3 2 9 4 6 7 2 1 5 9 8 3 4 8 1 6 3 5 7 4 9 2 8 3 4 1 5 9 6 7 2 real 0m0.209s user 0m0.200s sys 0m0.010s

Är detta verkligen alla tänkbara lösningar? Jag kan inte se enda där siffran 2 finns med i den mittersta kolumnen? Faktiskt inte heller i den mittersta raden?

Permalänk
Medlem
Skrivet av MickeBoy:

Är detta verkligen alla tänkbara lösningar? Jag kan inte se enda där siffran 2 finns med i den mittersta kolumnen? Faktiskt inte heller i den mittersta raden?

Det bör vara det. Ta en titt på wikipedia och algoritmen som står där.
Lägger du en tvåa i ett icke-hörn så kommer diagonalerna inte summera till rätt sak längre.

Permalänk
Skrivet av MickeBoy:

Är detta verkligen alla tänkbara lösningar? Jag kan inte se enda där siffran 2 finns med i den mittersta kolumnen? Faktiskt inte heller i den mittersta raden?

Ja, detta är alla lösningar.

"numbers = list(range(1,10))" ger en lista med tal från 1 till 9
"for x in itertools.permutations(numbers)" går igenom ALLA de 9! = 9*8*7*6*5*4*3*2*1 = 362880 olika sätten att ordna talen i listan.
Den långa if-satsen kollar om det är en giltig lösning.

Den här metoden är ingen höjdare för att hitta alla lösningar för 4x4 dock, eftersom den då måste gå igenom 16! = 20 922 789 888 000 (~21 miljoner miljoner) olika sätt att ordna talen. Om man räknar snällt och säger att det tar lika lång tid att testa ett fall som för 3x3 (vilket inte stämmer eftersom det är mer jobb att testa 4x4) så tar det ungefär 16! / 9! = 57657600 gånger längre tid.

Det tog ungefär 0,2 sekunder att testa alla fallen i 3x3. Det tar då ungefär (16! / 9!) * 0.2 = 11531520 sekunder vilket är ungefär 4 månader och 12 dagar.

Jag har satt igång programmet för 4x4. Det har inte gett nåt svar än. Återkommer i september

Permalänk
Medlem

Jag gjorde en lösning i C++ (mest C egentligen), visst är den snygg

#include <iostream> const int size = 4; const int max_num = size * size; const int magic = (size * (size * size + 1)) / 2; int add_number(int *row, int used_nums = 0, int pos = 1) { if(pos > 1 && (pos - 1) % size == 0) { int sum = 0; for(int i = 1; i <= size; ++i) { sum += *(row - i); } if(sum != magic) return 0; } if(pos > max_num) { for(int col = 0; col < size; ++col) { int sum = 0; for(int i = 1; i <= size; ++i) { sum += *(row + col - i * size); } if(sum != magic) return 0; } int diag_sum_1 = 0, diag_sum_2 = 0; int *row_2 = row - 1; for(int i = 0; i < size; ++i) { diag_sum_1 += *(row_2 - i * (size + 1)); } if(diag_sum_1 != magic) return 0; for(int i = 1; i <= size; ++i) { diag_sum_2 += *(row_2 - i * (size - 1)); } if(diag_sum_2 != magic) return 0; } if(pos > max_num) { for(int i = max_num; i > 0; --i) { if(i % size == 0) std::cout << std::endl; std::cout << *(row - i) << " "; } std::cout << std::endl; return 1; } int matches_found = 0; for(int i = 1; i <= max_num; ++i) { if(used_nums & (1 << i)) continue; *row = i; matches_found += add_number(row + 1, used_nums | (1 << i), pos + 1); } return matches_found; } int main() { int square[size*size]; std::cout << "Found: " << add_number(square) << " magic squares of size " << size << std::endl; }

Mitt program hittade i alla fall alla 4x4-lösningar. Wikipedia säger att det finns 880 st, och mitt program hittade 7040 st. Det går dock att rotera och spegla lösningarna på 8 olika sätt, och 7040 / 8 = 880. På samma sätt finns det endast en 3x3-lösning. Det tog i alla fall en bit över 11 minuter att hitta alla lösningar. Jag kan göra programmet lite effektivare genom att kontrollera kolumner lite tidigare, men jag tror inte det skulle ge så jättemycket. Det är också ganska trivialt att parallellisera algoritmen om man vill. Här är slutet av utskriften från en körning mätt med time:

16 14 3 1 7 4 13 10 2 11 6 15 9 5 12 8 16 15 1 2 4 3 13 14 5 10 8 11 9 6 12 7 16 15 2 1 4 3 14 13 5 10 7 12 9 6 11 8 16 15 2 1 5 3 14 12 4 10 7 13 9 6 11 8 Found: 7040 magic squares of size 4 real 11m8.903s user 11m8.713s sys 0m0.123s

Permalänk
Medlem

På 15 minuter klarade inte min sql-variant att lösa 4x4-problemet..

Permalänk

Här kommer kod i VB (Ni vet den där utdaterade fossilen)...

Klarar av 4x4 på ~5 sekunder (AMD X2 3800+, enkeltrådat, tid uppmätt med Process Explorer)...
Kernel: 0.046
User Time: 04.937
Total Time: 04.984

Option Explicit Private Const MAGIC_SIZE = 4 Private Const MAGIC_NUMBERS = MAGIC_SIZE * MAGIC_SIZE Private Const MAGIC_SUM = (MAGIC_SIZE * (MAGIC_SIZE * MAGIC_SIZE + 1)) \ 2 Private Type GROUPITEM Count As Long Sum As Long End Type Private m_BitValue(1 To 30) As Long Private m_Pos(1 To MAGIC_NUMBERS) As POINTL Private m_Data(1 To MAGIC_SIZE, 1 To MAGIC_SIZE) As Long Private m_Perms(1 To MAGIC_SIZE, 1 To MAGIC_SUM) As Long Private m_Group(1 To 2, 1 To MAGIC_SIZE + 1) As GROUPITEM Private Sub Command1_Click() Dim a As Long Dim x As Long Dim y As Long Dim CurrPosAdd As Boolean Dim CurrDirection As Long Command1.Enabled = False Call GetPerms(0, 0) For x = 1 To MAGIC_SIZE For y = 1 To MAGIC_SIZE m_Data(x, y) = 0 Next Next x = 0 y = 1 CurrDirection = 0 For a = 1 To MAGIC_NUMBERS CurrPosAdd = False Do Select Case CurrDirection Case 0 x = x + 1 If (x > MAGIC_SIZE) Then x = x - 1 CurrDirection = CurrDirection + 1 ElseIf (m_Data(x, y) = 1) Then x = x - 1 CurrDirection = CurrDirection + 1 Else CurrPosAdd = True End If Case 1 y = y + 1 If (y > MAGIC_SIZE) Then y = y - 1 CurrDirection = CurrDirection + 1 ElseIf (m_Data(x, y) = 1) Then y = y - 1 CurrDirection = CurrDirection + 1 Else CurrPosAdd = True End If Case 2 x = x - 1 If (x < 1) Then x = x + 1 CurrDirection = CurrDirection + 1 ElseIf (m_Data(x, y) = 1) Then x = x + 1 CurrDirection = CurrDirection + 1 Else CurrPosAdd = True End If Case 3 y = y - 1 If (y < 1) Then y = y + 1 CurrDirection = 0 ElseIf (m_Data(x, y) = 1) Then y = y + 1 CurrDirection = 0 Else CurrPosAdd = True End If End Select Loop Until (CurrPosAdd) m_Pos(a).x = x m_Pos(a).y = y m_Data(x, y) = 1 Next For x = 1 To MAGIC_SIZE For y = 1 To MAGIC_SIZE m_Data(x, y) = 0 Next Next Call Test Command1.Enabled = True End Sub Private Sub Test() Dim oo As Long Open "R:\Data.txt" For Output As #1 oo = GetTickCount() Print #1, "Found: " & GoNext() & " magic numbers of size 4" Print #1, "Time: " & GetTickCount() - oo & "ms" Close #1 End Sub Private Sub GoMatch() Print #1, MatrixToString() End Sub Private Function GoNext(Optional ByVal Pos As Long = 1, Optional ByVal GlobalPerms As Long = 0, Optional ByRef Counter As Long) As Long Dim a As Long Dim x As Long Dim y As Long Dim CurrPerms As Long 'Print out matching matrixes If (Pos > MAGIC_NUMBERS) Then Counter = Counter + 1 If (Counter Mod 100 = 0) Then Me.Caption = Counter DoEvents End If Call GoMatch Exit Function End If 'Resolve pos coordinates x = m_Pos(Pos).x y = m_Pos(Pos).y 'Start with global permuations (values used 'by the current stack call is removed) If (GlobalPerms = 0) Then GlobalPerms = 2 ^ (MAGIC_NUMBERS + 1) - 1 CurrPerms = GlobalPerms 'Restrict to permutatons only allowed 'by the same horizontal/vertical/diagonal line With m_Group(1, x) CurrPerms = CurrPerms And m_Perms(MAGIC_SIZE - .Count, MAGIC_SUM - .Sum) End With With m_Group(2, y) CurrPerms = CurrPerms And m_Perms(MAGIC_SIZE - .Count, MAGIC_SUM - .Sum) End With If (x = y) Then With m_Group(1, MAGIC_SIZE + 1) CurrPerms = CurrPerms And m_Perms(MAGIC_SIZE - .Count, MAGIC_SUM - .Sum) End With End If If ((MAGIC_SIZE + 1 - x) = y) Then With m_Group(2, MAGIC_SIZE + 1) CurrPerms = CurrPerms And m_Perms(MAGIC_SIZE - .Count, MAGIC_SUM - .Sum) End With End If 'Update the value counter of the same 'horizontal/vertical/diagonal lines With m_Group(1, x) .Count = .Count + 1 End With With m_Group(2, y) .Count = .Count + 1 End With If (x = y) Then With m_Group(1, MAGIC_SIZE + 1) .Count = .Count + 1 End With End If If ((MAGIC_SIZE + 1 - x) = y) Then With m_Group(2, MAGIC_SIZE + 1) .Count = .Count + 1 End With End If 'Enumerate allowed permutations For a = 1 To MAGIC_NUMBERS If ((CurrPerms And m_BitValue(a)) = m_BitValue(a)) Then m_Data(x, y) = a With m_Group(1, x) .Sum = .Sum + a End With With m_Group(2, y) .Sum = .Sum + a End With If (x = y) Then With m_Group(1, MAGIC_SIZE + 1) .Sum = .Sum + a End With End If If ((MAGIC_SIZE + 1 - x) = y) Then With m_Group(2, MAGIC_SIZE + 1) .Sum = .Sum + a End With End If Call GoNext(Pos + 1, GlobalPerms And (Not (m_BitValue(a))), Counter) With m_Group(1, x) .Sum = .Sum - a End With With m_Group(2, y) .Sum = .Sum - a End With If (x = y) Then With m_Group(1, MAGIC_SIZE + 1) .Sum = .Sum - a End With End If If ((MAGIC_SIZE + 1 - x) = y) Then With m_Group(2, MAGIC_SIZE + 1) .Sum = .Sum - a End With End If End If Next 'Update the value counter of the same 'horizontal/vertical/diagonal lines With m_Group(1, x) .Count = .Count - 1 End With With m_Group(2, y) .Count = .Count - 1 End With If (x = y) Then With m_Group(1, MAGIC_SIZE + 1) .Count = .Count - 1 End With End If If ((MAGIC_SIZE + 1 - x) = y) Then With m_Group(2, MAGIC_SIZE + 1) .Count = .Count - 1 End With End If 'Reset the cell m_Data(x, y) = 0 'Return the number of permutations If (Pos = 1) Then GoNext = Counter End If End Function Function MatrixToString() As String Dim a As Long Dim x As Long Dim y As Long For y = 1 To MAGIC_SIZE For x = 1 To MAGIC_SIZE MatrixToString = MatrixToString & m_Data(x, y) & " " Next MatrixToString = MatrixToString & vbCrLf Next End Function Private Function GetPerms(ByVal Value As Long, ByVal Count As Long) As Long Dim a As Long Dim CurrSum As Long For a = 1 To MAGIC_NUMBERS CurrSum = Value + a If (CurrSum <= MAGIC_SUM) Then m_Perms(Count + 1, CurrSum) = m_Perms(Count + 1, CurrSum) Or m_BitValue(a) If ((Count + 1) < MAGIC_SIZE) Then Call GetPerms(CurrSum, Count + 1) End If Next End Function Private Sub Form_Load() Dim a As Long For a = LBound(m_BitValue) To UBound(m_BitValue) m_BitValue(a) = 2 ^ a Next End Sub

Permalänk

Implementerade wikipedias metoder för udda storlekar! Någon som vet hur man gör 2d mappning snyggare?

def get_num(i, j, n): return n*((i+j-1+n/2)%n)+((i+2*j-2)%n)+1 def magic(n): if n%2!=1: raise Exception("Only works for odd numbers") for x in range(1, n+1): print map(lambda y: get_num(x, y, n), range(1, n+1))

Btw så kör detta såklart supersnabbt jämfört med allt annat här, hehe

Permalänk
Skrivet av jop_the_jopsan:

Btw så kör detta såklart supersnabbt jämfört med allt annat här, hehe

Det är nog bara jag som gillar att optimera sönder kod. Min kod går visserligen >134 ggr snababre än exemplet i C++ och ett par miljoner ggr snabbare än exemplet i Python, men den är kanske inte lika lättläst som de andra.