Kirjautuminen

Haku

Tehtävät

Keskustelu: Yleinen keskustelu: Algoritmi (ruudukon täyttö)

Sivun loppuun

Jaska [06.06.2008 13:58:32]

#

Miten kannattaisi toteuttaa seuraavan ongelman ratkaiseva ohjelma? Jos keksit ratkaisun todistukseen ilman algoritmia, sekin kelpaa.

On annettu 8x8-lauta {(i,j), 1 <= i,j <= 8}. Siihen laitetaan 1x3 ja 3x1 laattoja siten, että yksi ruutu jää peittämättä ja laatat eivät peitä toisiaan. Missä kohdassa peittämätön ruutu voi olla?

pipo [06.06.2008 15:56:01]

#

Jaska kirjoitti:

Jos keksit ratkaisun todistukseen ilman algoritmia, sekin kelpaa.

En tiä todistuksesta, mutta kynää ja paperia käyttäen sain ekalla yrityksellä ratkaisun.

| | | | | | | |
| | | | | | | |
| | | | | | | |
- - - | | - - -
- - - | | - - -
| | | | | x | |
| | | - - - | |
| | | - - - | |

Editoin että sähän haluut kaikki mahdolliset ratkaisut?

Jaska [06.06.2008 16:06:18]

#

pipo kirjoitti:

Editoin että sähän haluut kaikki mahdolliset ratkaisut?

Jeps. Yksittäisratkaisun minäkin löysin.

pipo [06.06.2008 16:23:48]

#

Laudalle jää väkisinkin vähintään yksi tyhjä kolo. Jotta kolosia olisi tasan yksi, täytyy palikat asetella näin:

| | - - -
| | - - -
| | x | |
- - - | |
- - - | |

Joku matemaatikko voi tämän kai jakojäännöksellä tms osoittaa, mutta noinhan se on.
Lopun laudan täyttämiseen on muutama vaihtoehto, mutta se ei ole oleellista. Yllä oleva 5x5 lauta sijoitetaan yhteen kohtaan* 8x8 lautaa ja loput täytetään annetuilla laatoilla. Sitten käännellään lautaa kolme kertaa ja näin löytyi neljä ratkaisua.

Jos joku tekee tästä koodin käyttämättä graafista ratkaisua -vektoreita tms- niin muakin kiinnostaisi nähdä se.

*mahtuu muuten vain siten että pieni lauta on isomman laudan kulmassa.

Pekka Karjalainen [06.06.2008 17:06:41]

#

Numeroidaan laudan ruudut yhdestä kolmeen seuraavalla tavalla.

12312312
31231231
23123123
12312312
31231231
23123123
12312312
31231231

Nyt jokaisen palikan sijoitus peittää kolme eri numeroa. Tämän näkee siitä, että joka rivillä ja sarakkeella on aina kahden saman numeron välissä kaksi muuta eri numeroa. Näin ollen 21:n palikan asettamisen jälkeen meillä on yksi ykkönen peittämättä.

Toisaalta laudan voi numeroida näin.

21321321
13213213
32132132
21321321
13213213
32132132
21321321
13213213

Sama argumentti kertoo, että taas jää yksi ykkönen peittämättä.

Vain sellaiset kohdat, mitkä on merkitty ykkösellä molemmissa taulukoissa voidaan jättää tyhjäksi jossakin ratkaisussa. Jatko jätetään lukijalle.

Antti Laaksonen [06.06.2008 18:32:50]

#

Jos et kaipaa mitään hienoa algoritmia, niin kaikkien vaihtoehtojen kokeilu kertoo vastauksen nopeasti, kun ruudukko on niin pieni.

Ohjelmani mukaan ratkaisuja on 1424 - onko joku muu saanut saman tuloksen?

Mahdollisista tyhjistä paikoista ohjelmani on ainakin samaa mieltä P. Karjalaisen kanssa.

Pekka Karjalainen [06.06.2008 19:30:37]

#

Tällaista tehtävää ratkaistessa yksinkertaisella ohjelmalla käytän mieluummin vaikkapa Pythonin sanakirjaa kuin taulukoita. Ei tarvitse niin hikoilla, että missä ne laidat ovat :)

Onhan tämä hidas! Menee useita sekunteja. Vaan jaksaako sitä välittää kerran ajettavan ohjelman nopeudesta?

squares = [(x,y) for x in range(1, 9) for y in range(1, 9)]
initial = dict(zip(squares, '.'*64))
empty, hole = ".x"
horiz, vert = 0, 1
sol_count = 0

def places(where, oriented):
    "Find the three places that a piece will fill."
    x, y = where
    if oriented == horiz: return [(x,y), (x+1,y), (x+2,y)]
    else: return [(x,y), (x,y+1), (x,y+2)]

def put_piece(board, where, oriented):
    "Add a piece on board and return True if possible. Otherwise False."
    mark = "-|"[oriented]
    pls = places(where, oriented)
    # are the places empty and on the board?
    for p in pls:
        if p not in board or board[p] != empty: return False
    # they are
    for p in pls:
        board[p] = mark
    return True

def rem_piece(board, where, oriented):
    "Remove a piece from board. Assumes we put it there correctly before."
    for p in places(where, oriented):
        board[p] = empty

def next_location(x, y):
    "Next place on board or None if at end."
    if (x,y) == (8,8): return None
    elif x == 8: return (1,y+1)
    else: return (x+1,y)

def print_board(board):
    "Print the board."
    global sol_count
    sol_count += 1
    place = (1,1)
    brd = "\n"
    while place is not None:
        brd += board[place]
        brd += " \n"[place[0]==8]
        place = next_location(*place)
    print brd

def depth_first_search(board, had_hole, placed, curr_loc):
    "Use the depth first search, Luke!"
    # had_hole indicates whether we've skipped over one spot already
    # placed tells us how many pieces we've placed; 21 and it's solved
    # curr_loc iterates from the first row to the right one step at a time
    if placed == 21:
        print_board(board)
        return
    # an empty place is guaranteed before we fall off the board
    # when there are less than 21 pieces placed
    while board[curr_loc] != empty:
        curr_loc = next_location(*curr_loc)
    for oriented in [horiz, vert]:
        if put_piece(board, curr_loc, oriented):
            depth_first_search(board, had_hole, placed + 1,
                               next_location(*curr_loc))
            rem_piece(board, curr_loc, oriented)
    # a solution must have only one hole in it
    if not had_hole:
        board[curr_loc] = hole
        depth_first_search(board, True, placed, next_location(*curr_loc))
        board[curr_loc] = empty
    return

if __name__ == '__main__':
    depth_first_search(initial, False, 0, (1,1))
    print "Found %d solutions" % sol_count

Tulee 1424 tälläkin.

Antti Laaksonen [06.06.2008 19:48:11]

#

Tuossa on minun ohjelmani:

#include <stdio.h>

int taulu[8][8];

int maara = 0;

void piirra(void) {
    int i, j;
    for (i = 0; i < 8; i++) {
        for (j = 0; j < 8; j++) {
            printf("%c", taulu[i][j]);
        }
        printf("\n");
    }
    printf("\n");
}

void haku(int y, int x, int n) {
    if (y == 8) {
        if (n == 21) {
            maara++;
            piirra();
        }
        return;
    }
    if (x == 8) {
        haku(y+1, 0, n);
        return;
    }
    if (taulu[y][x]) {
        haku(y, x+1, n);
        return;
    }
    if (x < 6 && !taulu[y][x+1] && !taulu[y][x+2]) {
        taulu[y][x] = taulu[y][x+1] = taulu[y][x+2] = '-';
        haku(y, x+3, n+1);
        taulu[y][x] = taulu[y][x+1] = taulu[y][x+2] = 0;
    }
    if (y < 6 && !taulu[y+1][x] && !taulu[y+2][x]) {
        taulu[y][x] = taulu[y+1][x] = taulu[y+2][x] = '|';
        haku(y, x+1, n+1);
        taulu[y][x] = taulu[y+1][x] = taulu[y+2][x] = 0;
    }
}

int main(void) {
    int i, j;
    for (i = 0; i < 8; i++) {
        for (j = 0; j < 8; j++) {
            taulu[i][j] = ' ';
            haku(0, 0, 0);
            taulu[i][j] = 0;
        }
    }
    printf("%i\n", maara);
}

Ohjelmaa voisi vielä nopeuttaa ottamalla huomioon ruudukon symmetriat.

Jaska [06.06.2008 20:35:50]

#

Hetkinen. Siis 1424 ratkaisua. Eikös 8x8 laudalla ole vain 64 ruutua, jossa se puuttumaton ruutu voi olla? Ja eikös ne paikat löydy niistä kohdista, joissa Pekka Karjalaisen antamissa numeroruudukoissa ole ykköset kohdikkain?

Antti Laaksonen [06.06.2008 20:42:29]

#

Siis on 1424 eri 1x3- ja 3x1-laattojen sijoitustapaa, joissa koko ruudukko on täytetty yhtä ruutua lukuun ottamatta. Laskimme siis saman tien vähän enemmän kuin alun perin kysyit.

Jaska [06.06.2008 20:54:15]

#

Ah. Niinpä tietysti.

pipo [06.06.2008 22:04:31]

#

Mun mielestä neljä oli ihan sopivasti d8)

Saavatko laatat ylittää laudan reunan? Jos saavat, niin eihän laudan kasvamisella olisi mitään väliä(?)

teksturi [07.06.2008 15:41:33]

#

Eikös se peittämätön ruutu voi olla missä tahansa kohtaa lautaa.

TsaTsaTsaa [07.06.2008 16:08:03]

#

teksturi kirjoitti:

Eikös se peittämätön ruutu voi olla missä tahansa kohtaa lautaa.

Voi, mutta ei niin, että sitten olisi ainoastaan yksi peittämätön ruutu.

Pekka Karjalainen [07.06.2008 17:32:09]

#

Jos numeroargumenttini on pätevä, niin seuraavanlaiset neliölaudat voivat sisältää koloja merkityissä kohdissa. Kolmella jaollinen laidan pituus johtaa siihen, että koloa ei voi olla ollekaan, joten ne on jätetty tästä pois. Laudalla kokoa 2x2 ei voi olla yhtään palikkaa, ja 1x1 on jätetty itse kunkin mietittäväksi.

x . . x
. . . .
. . . .
x . . x

. . . . .
. . . . .
. . x . .
. . . . .
. . . . .

x . . x . . x
. . . . . . .
. . . . . . .
x . . x . . x
. . . . . . .
. . . . . . .
x . . x . . x

. . . . . . . .
. . . . . . . .
. . x . . x . .
. . . . . . . .
. . . . . . . .
. . x . . x . .
. . . . . . . .
. . . . . . . .

x . . x . . x . . x
. . . . . . . . . .
. . . . . . . . . .
x . . x . . x . . x
. . . . . . . . . .
. . . . . . . . . .
x . . x . . x . . x
. . . . . . . . . .
. . . . . . . . . .
x . . x . . x . . x

Näyttäisi siis, että mahdolliset kolot ovat harvan ristikon muotoisessa kuviossa, jonka keskipiste on samassa kohdassa kuin laudan keskipiste. Joku fiksu ja ahkera voisi miettiä, mitä tapahtuu suorakulmaisilla laudoilla eli kokoa mxn olevilla, missä m != n.

Noita varten tein pienen ohjelman. Toivottavasti meni oikein ;-)

(Pienet laudat voi ainakin varmistaa itsekin kokeilemalla.)

pipo [07.06.2008 18:02:03]

#

pipo kirjoitti:

| | - - -
| | - - -
| | x | |
- - - | |
- - - | |

Pekka Karjalainen kirjoitti:

. . . . . . . .
. . . . . . . .
. . x . . x . .
. . . . . . . .
. . . . . . . .
. . x . . x . .
. . . . . . . .
. . . . . . . .

Kiitos tulkinnasta! Edelleen neljä ratkaisua(?) Noita ruudukoita on sangen antoisaa kirjoitella.

| | - - - . . .
| | - - - . . .
| | x | | x . .
- - - | | . . .
- - - | | . . .
. . x . . x . .
. . . . . . . .
. . . . . . . .

jormi [08.06.2008 09:54:23]

#

Käänsin Antti Laaksosen ohjelman lcc-win32 kääntäjälläni (1.1sec). Lisäsin loppuun pysäytyksen nähdäkseni lopputuloksen. Ongelma muistuttaa hiukan kauan sitten käymieni Fortran-kurssien shakkiruudukkotehtäviä. Aion tutkia ohjelman toimintaa debuggerin avulla. JVM


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta