Forum Coders' city Strona Główna Coders' city
Nasza pasja to programowanie!
 

 PomocPomoc   SzukajSzukaj   UżytkownicyUżytkownicy   GrupyGrupy  RejestracjaRejestracja 
Archiwum starego forum + teoria    RSS & Panel/SideBar
 ProfilProfil   Zaloguj się, by sprawdzić wiadomościZaloguj się, by sprawdzić wiadomości   ZalogujZaloguj 

Potrzebuję szybkiej odpowiedzi na moje pytanie... Skrócony regulamin

Magiczny kwadrat - jak go stworzyć?



 
Odpowiedz do tematu    Forum Coders' city Strona Główna -> Algorytmy + inżynieria oprogramowania
Zobacz poprzedni temat :: Zobacz następny temat  
Autor Wiadomość
Kayne



Dołączył: 25 Sie 2005
Posty: 236

PostWysłany: Pon Lis 07, 2011 3:16 pm      Temat postu: Magiczny kwadrat - jak go stworzyć? Odpowiedz z cytatem Pisownia

Witam,

Zastanawiałem się dłuższy czas, czy zakładać temat w tym dziale, czy w C i C++, ale ostatecznie stwierdziłem, że bardziej potrzebuję teoretycznej wiedzy, czy naprowadzenia na rozwiązanie. Póki co, nie mam problemów w kodzie, bo go nie mam ;)

Do rzeczy: W jaki sposób wygenerować magiczny kwadrat? Nie mogłem w sieci znaleźć żadnych ciekawych, mądrych sposobów ani algorytmów na to. Jedyne co mi przychodzi na myśl, to generowanie kwadratów z losowymi liczbami i sprawdzanie, czy są to magiczne kwadraty, ale... Jest to strasznie nie wydajne rozwiązanie i musi istnieć coś lepszego.

Ktoś ma może jakieś pomysły?
Powrót do góry
Zobacz profil autora Wyślij prywatną wiadomość Odwiedź stronę autora Numer GG
marcin_an
Site Admin


Dołączył: 26 Maj 2005
Posty: 17278
Skąd: z drugiej strony Kabla

PostWysłany: Pon Lis 07, 2011 4:08 pm      Temat postu: Odpowiedz z cytatem Pisownia

pierwszy i drugi wynik
_________________
Matematyka to taki twór, który pozwala opisać sokowirówkę jako urządzenie pobierające ujemne odpadki i produkujące z nich sok.
"Lameria atakuje" | RTFM | UMLet - edytor UML inaczej | Wykłady ks.Pawlukiewicza
Powrót do góry
Zobacz profil autora Wyślij prywatną wiadomość
hurgadion



Dołączył: 06 Kwi 2011
Posty: 143
Skąd: Kraków

PostWysłany: Pon Lis 07, 2011 6:25 pm      Temat postu: Odpowiedz z cytatem Pisownia

Witam,
może losowe sposoby nie są zbyt wydajne, ale przy tworzeniu kwadratu magicznego 3x3 losowy sposób działa dość szybko, o ile zastosujemy pomocniczą funkcję losującą unikalne liczby z zakresu 1-9 (funkcja RndInt na podstawie Walkenbacha). Moje rozwiązanie jest w VBA, ale pewnie mozna go spokojnie przenieść na inny język programowania. Kod ma postać:
Kod:

Sub MagicalSquare()
Dim tbl(), a&, b&, c&, d&, e&, f&, g&, h&

ReDim tbl(1 To 9)

Randomize

For i = 1 To 100000
tbl = RndInt

a = tbl(1) + tbl(2) + tbl(3)
b = tbl(4) + tbl(5) + tbl(6)
c = tbl(7) + tbl(8) + tbl(9)
d = tbl(1) + tbl(4) + tbl(7)
e = tbl(2) + tbl(5) + tbl(8)
f = tbl(3) + tbl(6) + tbl(9)
g = tbl(1) + tbl(5) + tbl(9)
h = tbl(3) + tbl(5) + tbl(7)

If a = 15 And b = 15 And c = 15 And d = 15 And e = 15 And f = 15 And g = 15 And h = 15 Then
  Cells(1, 1).Value = tbl(1)
  Cells(1, 2).Value = tbl(2)
  Cells(1, 3).Value = tbl(3)
  Cells(2, 1).Value = tbl(4)
  Cells(2, 2).Value = tbl(5)
  Cells(2, 3).Value = tbl(6)
  Cells(3, 1).Value = tbl(7)
  Cells(3, 2).Value = tbl(8)
  Cells(3, 3).Value = tbl(9)
  Exit Sub
End If

Next i
End Sub


Function RndInt()
Dim V() As Variant, Val As Variant
Dim i&, j&, r&, c&, a&
Dim t1 As Variant, t2 As Variant

Randomize

a = 9

ReDim V(1 To a)
ReDim Val(1 To 2, 1 To a)

For i = 1 To a
  Val(1, i) = Rnd
  Val(2, i) = i
Next i

For i = 1 To a
  For j = i + 1 To a
    If Val(1, i) > Val(1, j) Then
      t1 = Val(1, j)
      t2 = Val(2, j)
      Val(1, j) = Val(1, i)
      Val(2, j) = Val(2, i)
      Val(1, i) = t1
      Val(2, i) = t2
     End If
   Next j
Next i
  
i = 0
For r = 1 To a
    i = i + 1
    V(i) = Val(2, i)
Next r

RndInt = V
End Function


Funkcja RndInt jest pomocnicza, natomiast główne makro nie jest optymalne, pozdrawiam.



MagicalSquare.rar
 Opis:

Pobierz
 Nazwa pliku:  MagicalSquare.rar
 Wielkość pliku:  10.16 KB
 Pobierano:  26 raz(y)



Ostatnio zmieniony przez hurgadion dnia Pon Lis 07, 2011 9:23 pm, w całości zmieniany 1 raz
Powrót do góry
Zobacz profil autora Wyślij prywatną wiadomość Odwiedź stronę autora Numer GG
Hashedone



Dołączył: 23 Sie 2008
Posty: 853

PostWysłany: Pon Lis 07, 2011 7:36 pm      Temat postu: Odpowiedz z cytatem Pisownia

3x3 to przypadek niemalże trywialny, wygeneruj 4x4. W swi-prologu z clpfd (technologia więzów dla skończonych zbiorów naturalnych) generowało się kilka sekund (na dwurdzeniowym P6000 bodajże), a z doświadczenia wiem, że trudno stworzyć samemu coś bardziej wydajnego dla tego typu problemów (co nie znaczy że się nie da). 5x5 mi się wygenerować w ogóle nie udało (jak będę miał chwilę to spróbuję w jakimś szybszym prologu bo to faktycznie nie jest najwydajniejsza implementacja). Życzę powodzenia, ale nie wiem czy są na to wydajne i w miarę proste algorytmy;)
_________________
PWr, WPPT, Informatyka
"Two or more? - use a for", Dijkstra
Powrót do góry
Zobacz profil autora Wyślij prywatną wiadomość
Kayne



Dołączył: 25 Sie 2005
Posty: 236

PostWysłany: Pon Lis 07, 2011 8:42 pm      Temat postu: Odpowiedz z cytatem Pisownia

hurgadion, dzięki, ale z 3x3 kwadratem bym sobie szybko poradził (świetny sposób Yang Hui). Problem w tym, że musi się sprawdzać dla każdych ;)

Hashedone, tutaj bardziej chodzi o sam zapis kodu, jakiś algorytm niż fakt, czy jest super wydajny (no, może po za tą pełną losowością... ;) ).

marcin_an, szukałem na angielskiej wiki, ale nie znalazłem zbytnio niczego, co by mi pomogło - albo umiałbym zakodować. Właśnie przejrzałem drugi wynik z google... I też zbytnio mi nie pomógł. Po prostu nie widzę, jak miałbym to w C napisać.

Dlatego szukam dalej rozwiązań, które sprawdzają się dla dowolnego n.
Powrót do góry
Zobacz profil autora Wyślij prywatną wiadomość Odwiedź stronę autora Numer GG
hurgadion



Dołączył: 06 Kwi 2011
Posty: 143
Skąd: Kraków

PostWysłany: Pon Lis 07, 2011 9:56 pm      Temat postu: Odpowiedz z cytatem Pisownia

W sieci PL nie za wiele jest na temat algorytmizacji tego problemu, znalazłem coś takiego: http://math.uni.lodz.pl/~radmat/sztuczna/Projekt3.pdf więc chyba się da dla n > 4, chyba że prowadzący rzucił otwarty problem studentom :)

Hashedone: Odpaliłem losową metodę dla kwadratu 4x4, po kilkunastu minutach bez rezultatu, jutro jak będę miał humor, to odpalę kompa może na nieco dłużej... :)
Powrót do góry
Zobacz profil autora Wyślij prywatną wiadomość Odwiedź stronę autora Numer GG
Mgr.Dobrowolski



Dołączył: 18 Cze 2006
Posty: 475

PostWysłany: Pon Lis 07, 2011 11:22 pm      Temat postu: Odpowiedz z cytatem Pisownia

prawiem pewny, że widziałem na IOCCC, a tam programy są raczej zwięzłe.
Powrót do góry
Zobacz profil autora Wyślij prywatną wiadomość Wyślij email Numer GG Tlen
Hashedone



Dołączył: 23 Sie 2008
Posty: 853

PostWysłany: Wto Lis 08, 2011 11:13 am      Temat postu: Odpowiedz z cytatem Pisownia

@hugarion - źle się wyraziłem. Da się to zrobić i to całkiem szybko, jest o tym na drugim wyniku spod linku marcin_ana. Problem jest inny - dla mnie "algorytm generowania X" jest fajny dopiero wtedy, kiedy mogę za pomocą tego algorytmu wygenerować wszystkie X. Okazuje się że można to zrobić całkiem nieźle tą metodą której używałem przy użyciu lepszego prologa - w GNU Prologu na kwadrat 16x16 czekałem niecałe 26s. Podejżewam że yap byłby jeszcze szybszy, ale nie chce mi się ogarniać jego bibliteki do clpfd.

Co do algorytmu to jest w sumie prosty. Niech Square będzie tablicą nxn zbiorów liczb naturalnych (te zbiory będziemy nazywali dziedzinami). Początkowo każdy element Square zawiera wszystkie liczby z przedziału [1; n*n]. Weźmy sobie funkcję:
Kod:

function magic_matrix(Square)
1. Zawęź wszystkie dziedziny ze Square
2. Jeśli któraś dziedzina jest pusta, zwróć FAIL
3. Jeśli wszystkie dziedziny są jednoelementowe, zwróć Square
4. D = najmniejsza nie-jednoelementowa dziedzina Square
5. X = losowy element z D
6. NextSquare = Square, z zamienioną dziedziną D na {X}
7. Result = magic_matrix(NextSquare)
8. Jeśli Result != FAIL, zwróć Result
9.  Usuń X z D
10. Idź do (2)


Jeśli można wywołać tę funkcję tak, aby zaczęła się dokładnie od miejsca gdzie ostatnio zwróciła wartość (wraz z kompletnym zachowaniem stosu), zwróci nam w końcu wszystkie możliwe kwadraty. W tym wszystkim najważniejszy jest punkt (1). Na czym to polega? Powiedzmy sobie że szukamy trzech liczb A, B, C, każda z przedziału [1;10] takich, że A + B + C = 20. Początkowo dziedziny A, B, C = [1; 10]. Jednak jeśli założymy że dziedziną A = {1}, to dziedziny B, C = [9; 10], bo jeśli pod którąkolwiek z tych zmiennych będzie coś < 9, to nie da rady dobrać drugiej liczby żeby warunek był spełniony. Dla magicznych kwadratów będziesz miał bardzo podobnie - dla każdego pola trzeba określić liczby, które jeszcze się mogą tam znaleźć. To taki uproszczony opis technologii więzów, polecam poczytać. Z góry uprzedzę że nie wiem czy to jest najlepsza metoda - wiem że działa i to całkiem nieźle i uważam ją za najlepsze podejście do problemu (przede wszystkim dla tego że algorytm jest bardzo ogólny i nie trzeba go całego pisać, tylko wystarczy wziąść do ręki odpowiednie narzędzie).
Jeszcze takie uwagi co do dwóch punktów:
(4) - nie zawsze bierze się najmniejszą dziedzinę. Można brać największą, najbardziej związaną (czyli taką, na której jest nawięcej warunków, można je brać w z góry ustalonej kolejności, lub nawet losowo. Jednak w moich próbach najlepsze rezultaty dało branie najmniejszej dziedziny (generalnie dla większości problemów jest to najmniejsza heurystyka, czasem przegrywa z braniem najbardziej związanej dziedziny)
(5) - znowu, nie zawsze losowy. Można brać najmniejszy, największy, środkowy, średni... Znowu metodą sprawdzania różnych heurystyk doszedłem do tego, że ta daje najlepsze rezultaty (i znowu - jest to typowe dla problemów, gdzie wyniki wyglądają na chaotyczne).
Oczywiście przedstawiony algorytm daje bardzo duże pole do optymalizacji. Można pozbyć się rekursji, można przede wszystkim na masę sposobów zrealizować punkt (1), jednak ten post i tak jest już za długi:D Polecam doczytać o technologii więzów i programowaniu w logice;)

_________________
PWr, WPPT, Informatyka
"Two or more? - use a for", Dijkstra
Powrót do góry
Zobacz profil autora Wyślij prywatną wiadomość
hurgadion



Dołączył: 06 Kwi 2011
Posty: 143
Skąd: Kraków

PostWysłany: Czw Lis 10, 2011 7:08 pm      Temat postu: Odpowiedz z cytatem Pisownia

Podszedłem do tematu hobbystycznie i napisałem programik wyszukujący kwadrat magiczny 4 x 4, działa nie za szybko, ale wyszukuje skutecznie (na dziesięć prób wyszukuje średnio 5,5 minuty). Kod jest nieco inny jak wyżej (plus funkcja RndInt()), może kogoś zainteresuje ten sposób:
Kod:

Sub Kwadrat4()
Dim z&, tab16(), tbl12(), tbl8(), a1&, a2&, a3&, a4, a5&, a6&, tbl(), i&, m&
Dim tbl1(), tbl2(), tbl3(), tbl4(), t(), t1(), t2(), t3(), t4(), w As Single, s As Single

Randomize

dalej:
z = 1
Do While z <> 0
  tab16 = RndInt(16)
  a1 = tab16(1) + tab16(2) + tab16(3) + tab16(4)
  If a1 = 34 Then z = 0
Loop

ReDim tbl1(1 To 4)
For i = 1 To 4
  tbl1(i) = tab16(i)
Next i

ReDim tbl12(1 To 12)
For i = 1 To 12
  tbl12(i) = tab16(i + 4)
Next i

ReDim tablica(1 To 12)
z = 1

Do While z <> 0
  tbl = RndInt(12)
  tablica = tbl12
  For i = 1 To 12
    tbl12(i) = tablica(tbl(i))
  Next i
  a1 = tbl12(1) + tbl12(2) + tbl12(3) + tbl12(4)
  If a1 = 34 Then z = 0
Loop

ReDim tbl2(1 To 4)
For i = 1 To 4
  tbl2(i) = tbl12(i)
Next i

ReDim tbl8(1 To 8)
For i = 1 To 8
  tbl12(i) = tbl12(i + 4)
Next i

ReDim tablica(1 To 8)
z = 1

Do While z <> 0
  tbl = RndInt(8)
  tablica = tbl12
  For i = 1 To 8
    tbl8(i) = tablica(tbl(i))
  Next i
  a1 = tbl8(1) + tbl8(2) + tbl8(3) + tbl8(4)
  If a1 = 34 Then z = 0
Loop

ReDim tbl3(1 To 4)
ReDim tbl4(1 To 4)
For i = 1 To 4
  tbl3(i) = tbl8(i)
  tbl4(i) = tbl8(i + 4)
Next i

z = 1
m = 0
Do While z <> 0
  t1 = RndInt(4)
  t2 = RndInt(4)
  t3 = RndInt(4)
  t4 = RndInt(4)
  t = tbl1
  For i = 1 To 4
    tbl1(i) = t(t1(i))
  Next i
  t = tbl2
  For i = 1 To 4
    tbl2(i) = t(t2(i))
  Next i
  t = tbl3
  For i = 1 To 4
    tbl3(i) = t(t3(i))
  Next i
  t = tbl4
  For i = 1 To 4
    tbl4(i) = t(t4(i))
  Next i
  a1 = tbl1(1) + tbl2(1) + tbl3(1) + tbl4(1)
  a2 = tbl1(2) + tbl2(2) + tbl3(2) + tbl4(2)
  a3 = tbl1(3) + tbl2(3) + tbl3(3) + tbl4(3)
  a4 = tbl1(4) + tbl2(4) + tbl3(4) + tbl4(4)
  a5 = tbl1(1) + tbl2(2) + tbl3(3) + tbl4(4)
  a6 = tbl1(4) + tbl2(3) + tbl3(2) + tbl4(1)
  If Application.Min(a1, a2, a3, a4, a5, a6) = 34 And Application.Max(a1, a2, a3, a4, a5, a6) = 34 Then z = 0
  m = m + 1
  If m > 200000 Then GoTo dalej:
Loop

Cells(1, 1).Resize(, 4) = tbl1
Cells(2, 1).Resize(, 4) = tbl2
Cells(3, 1).Resize(, 4) = tbl3
Cells(4, 1).Resize(, 4) = tbl4

End Sub



PS. Kupiłem sobie Hashedone książkę o Prologu, czekam na przesyłkę :)



Kwadrat.mag.4x4.rar
 Opis:

Pobierz
 Nazwa pliku:  Kwadrat.mag.4x4.rar
 Wielkość pliku:  12.92 KB
 Pobierano:  13 raz(y)

Powrót do góry
Zobacz profil autora Wyślij prywatną wiadomość Odwiedź stronę autora Numer GG
Wyświetl posty z ostatnich:   
Odpowiedz do tematu    Forum Coders' city Strona Główna -> Algorytmy + inżynieria oprogramowania Wszystkie czasy w strefie CET (Europa)

Strona 1 z 1

 
Skocz do:  
Możesz pisać nowe tematy
Możesz odpowiadać w tematach
Nie możesz zmieniać swoich postów
Nie możesz usuwać swoich postów
Nie możesz głosować w ankietach
Możesz dodawać załączniki na tym forum
Możesz pobierać pliki z tego forum




Debug: strone wygenerowano w 0.09393 sekund, zapytan = 9
contact

| Darmowe programy i porady Jelcyna | VB4all | Tansze zakupy w Helionie |