Showing posts with label Puzzle. Show all posts
Showing posts with label Puzzle. Show all posts

Sunday, May 30, 2010

Learning F# with Project Euler - day 2

Problem 4

open Microsoft.FSharp.Core.Operators
let findpalindrome xs ys =
    let separate s p = (s % (pown 10 (p+1))) - (s % (pown 10 p))
    let shiftl s p o = (separate s p) / (pown 10 (p-o))
    let shiftr s p o = (separate s p) * (pown 10 (o-p))
    let flip f = shiftl f 5 0 + shiftl f 4 1 + shiftl f 3 2 + shiftr f 2 3 + shiftr f 1 4 + shiftr f 0 5
    let findi xs y = xs |> Seq.filter( fun x -> (((x*y) = flip (x*y)) && (separate (x*y) 0 <> 0) ) ) |> Seq.map(fun x -> (x*y,x))
    ys |> Seq.collect(fun y -> (findi xs y)) |> Seq.maxBy(fun t -> fst(t))

findpalindrome [100..999][100..999]

Problem 5

let test = 
    let max = (primes 20 |> Seq.reduce(fun a c -> a*c))*8L*3L
    [1L..20L] |> Seq.filter(fun x -> (max % x) <> 0L)

Problem 6

let diff xs = 
    let sumsq = xs |> Seq.reduce(fun c a -> (a*a)+c)
    let sum = (xs |> Seq.reduce(fun c a -> (a+c)))
    let sqsum  = sum*sum
    sqsum  - sumsq

diff [1..100]

Problem 7

open Microsoft.FSharp.Core.Operators
let primesnth nthprime = 
    let maxPrime = 300000
    let pr : bool array = Array.zeroCreate maxPrime
    let mutable res = 0
    let mutable nth = 0
    let mutable cur=2
    while (nth < nthprime) do
        if not pr.[cur] then
            for wr in cur+cur .. cur .. maxPrime-1 do pr.[wr] <- true
            res <- cur
            nth <- nth + 1 
        cur <- cur + 1
    res

primesnth 10001

Friday, May 28, 2010

Learning F# with projecteuler.net

Wow, lot of fun with projecteuler.net !

Problem 1

[1..999] |> Seq.filter( fun x -> (x % 3 = 0 || x % 5 = 0)) |> Seq.sum

Problem 2

let fib max = 
    let rec fibo a b max = 
        if b>= max then a :: [] else a :: fibo b (a+b) max
    fibo 1 2 max

fib 4000000 |> Seq.filter(fun x -> (x % 2 = 0)) |> Seq.toList|> Seq.sum

Problem 3

open Microsoft.FSharp.Math 
open System.Collections.Generic
let maxPrime:int= (int) (System.Math.Sqrt((float)600851475143I))
let primes maxPrime = 
    let pr : bool array = Array.zeroCreate maxPrime
    let res = new List()
    for cur in 2 .. maxPrime/2 do
        if not pr.[cur] then for wr in cur+cur .. cur .. maxPrime-1 do pr.[wr] <- true
    for cur in 1 .. maxPrime-1 do
        if not pr.[cur] then res.Add(new bigint(cur))
    res

primes maxPrime |> 
    Seq.filter(fun x -> ((600851475143I % x) = 0I )) 
    |> Seq.map( fun x -> ((int)(x))) 
    |> Seq.max

Tuesday, January 20, 2009

English peg solitaire

My colleague at work mentioned that there is some puzzle where we have 33 holes in shape of cross and 32 bullets in all the holes but central. To move you should skip one bullet with it's neighbor into empty hole and remove the skipped one. The goal is to clean all but last bullet. He mentioned that it would be interesting to find solution with computer.
And we didn't knew the name of the game. Now it took me almost an hour to find it out for purpose of this article. So now when you know the name of the puzzle, you can easily find the solution on Internet. Actually quite interesting reading.

But I enjoyed brute force search for solution. Below is solution I found after few hours of coding and 6 minutes of intensive computing.

  XXX     XXX     XXX     XXX     XXX     XXX     XX0     XX0   
  XXX     XXX     0XX     0XX     0XX     0XX     0X0     0XX   
XXXXXXX XXXXXXX XX0XXXX 00XXXXX 0X00XXX 0X0X00X 0X0XX0X 0X0X00X 
XXX0XXX X00XXXX X0XXXXX X0XXXXX X0XXXXX X0XXXXX X0XXXXX X0XX0XX 
XXXXXXX XXXXXXX XXXXXXX XXXXXXX XXXXXXX XXXXXXX XXXXXXX XXXXXXX 
  XXX     XXX     XXX     XXX     XXX     XXX     XXX     XXX   
  XXX     XXX     XXX     XXX     XXX     XXX     XXX     XXX   
  
  XX0     XX0     XX0     XX0     XX0     XX0     XX0     XX0  
  0XX     0XX     0XX     0XX     0XX     0XX     0XX     0XX  
0X0X00X 0X0X00X 0X0X00X 0X0X000 0X0X000 0X0X000 XX0X000 00XX000
X0XXXXX X0XXXXX X0XXXXX X0XXXX0 X0XXXX0 X0XX0X0 00XX0X0 00XX0X0
XXXX0XX XXXXX00 XXX00X0 XXX00XX XXX0X00 XXX0000 0XX0000 0XX0000
  XX0     XX0     XX0     XX0     XX0     XXX     XXX     XXX  
  XXX     XXX     XXX     XXX     XXX     XXX     XXX     XXX  

  XX0     XX0     00X     00X     000     000     000     000  
  0XX     0XX     0XX     0XX     0X0     XX0     XX0     XX0  
00XX000 00XX000 00XX000 00XX000 00XXX00 000XX00 00X0000 00X0000
00XX0X0 00XX0X0 00XX0X0 00XX0X0 00XX0X0 000X0X0 000X0X0 000X0X0
0XX0X00 0XXXX00 0XXXX00 0XX00X0 0XX00X0 0XX00X0 0XX00X0 000X0X0
  XX0     X00     X00     X00     X00     X00     X00     X00  
  XX0     X00     X00     X00     X00     X00     X00     X00  

  000     000     000     000     000     000     000     000  
  XX0     0X0     0X0     0X0     0X0     0X0     0X0     000  
00XX000 000X000 000X000 00XX000 00XX0X0 0000XX0 000X000 0000000
00000X0 00X00X0 00X00X0 00000X0 0000000 0000000 0000000 000X000
00000X0 00000X0 00X00X0 00000X0 0000000 0000000 0000000 0000000
  X00     X00     000     000     000     000     000     000  
  X00     X00     000     000     000     000     000     000  

And there is the code. I renamed it to peg solitaire afterwards. I used C5 library for HashDictionary, but I'm not sure whether it was really necessary. And yes it takes some memory to compute the results as do breadth-first search. I don't even try any clever heuristics or cutting the tree.

Saturday, December 1, 2007

Paranoid Android

By 446F75676C6173204164616D73
2249207468696E6B20796F75206F7567687420746F206B6E6F772049276D2066
65656C696E672076657279206465707265737365642C2220697420736169642E
0A49747320766F69636520776173206C6F7720616E6420686F70656C6573732E
0A224F6820476F642C22206D75747465726564205A6170686F6420616E642073
6C756D70656420696E746F206120736561742E0A2257656C6C2C222073616964
205472696C6C69616E20696E20612062726967687420636F6D70617373696F6E
61746520746F6E652C202268657265277320736F6D657468696E6720746F206F
636375707920796F7520616E64206B65657020796F7572206D696E64206F6666
207468696E67732E220A22497420776F6E277420776F726B2C222064726F6E65
64204D617276696E2C202249206861766520616E20657863657074696F6E616C
6C79206C61726765206D696E642E220A224D617276696E2122207761726E6564
205472696C6C69616E2E0A22416C72696768742C222073616964204D61727669
6E2C20227768617420646F20796F752077616E74206D6520746F20646F3F220A
22476F20646F776E20746F206E756D6265722074776F20656E74727920626179
20616E64206272696E67207468652074776F20616C69656E7320757020686572
6520756E646572207375727665696C6C616E63652E220A576974682061206D69
63726F7365636F6E642070617573652C20616E6420612066696E656C79206361
6C63756C61746564206D6963726F6D6F64756C6174696F6E206F662070697463
6820616E642074696D627265202D206E6F7468696E6720796F7520636F756C64
2061637475616C6C792074616B65206F6666656E6365206174202D204D617276
696E206D616E6167656420746F20636F6E766579206869732075747465722063
6F6E74656D707420616E6420686F72726F72206F6620616C6C207468696E6773
2068756D616E2E0A224A75737420746861743F2220686520736169642E0A2259
65732C222073616964205472696C6C69616E206669726D6C792E0A224920776F
6E277420656E6A6F792069742C222073616964204D617276696E2E

Tuesday, November 13, 2007

Cube21 Silver edition - database of all solutions

Cube21 or Square One is puzzle similar to Rubik's Cube. I'm proud owner of one from Silver edition (which have only two colors) since year 2000. Regular Cube21 has 6 colors. This cube is different from Rubik's mainly by changing it's shape. There are 90 distinct shapes. Good analysis could be found here so I don't need to repeat that now, more Cube21 links below.

Cube21
Because I was always more interested in programming than solving the cube by hands, I started to solve the cube by program. Soon I realized that deep search with some heuristics is not that funny. Same time my friend Berka provoked me to create something more challenging, the database of all shortest solutions.

Now after years I returned to that idea to exercise in new technologies. I implemented analytic part of the work in C#, brute force part in C++/CLI. User interface is done in 3D WPF. Because you will probably not want generate whole database at home, I connected the clients to database on my machine using WCF web service. Finally some of you may not have windows platform at all, but you would like to use the database. So for you I created simple ASP+AJAX form. Now some video of the client GUI solving the cube.

Model

So let's describe the model: cube has 16 pieces (8 small + 8 big). Those small are only of 4 types as my cube has only 2 colors. Big ones are unique. On middle layer, there are 2 halves, connected with axis. When you turn around the axis you must finish 180 degrees, so it has only two states. *We don't need to care about middle layer.* <- Update at the bottom.

Cube21
Now let's assign pieces some (hexadecimal) numbers. It's anticlockwise, first on top then on bottom half. Numbers are assigned on solved cube, so that 01234567-89ABCDEF is sequence of solved cube. Detailed model and guide to your puzzled cube here.

Note that small pieces are there twice so we have permutation of multiset Small 004488CC and big 13579BDF, this will give us 2.2*10^13 permutations. Let's also say that we could consider same all cube configurations which could be transformed to each other without turning left and right half of the cube. So rotation of top, bottom and flipping whole cube is still the same cube. Last note that we will mark solved cube with level 1, there is only cube on that level. When performed any operations with top, bottom and finally make turn of right part, we reached level 2. We could found all cubes on level 2 by doing all possible operations on cube on level 1. Level 3 will be result of all operations on all cubes on level 2, which are not on level 1 or 2. Similarly for other levels.

Notation of moves is (top, bottom), '/' is turn right half, '!' is flip whole cube. For both top and bottom rotation, positive is anticlockwise.

-- Here was Ajax frame with webservice to database. Is gone now.

Algorithmization

This is the best part, rest of the work was just cheery on the cake. My first idea years ago was very naive: do operations on cube model, then search whether cube is in the list of already found cubes and if not add it into the list. This doesn't work long, because growing list of cubes will be slower and slower. So, sort it and binary search ? Not in memory, because count of cubes is cca 6.8*10^9, that could not fit into 32bit memory space. Ok, we could B-tree on disk, maybe even with some existing database engine.

But there is better solution. For each cube configuration = permutation we could assign index of permutation. On such index we will store just level of the cube. But 2.2*10^13 permutations will give us 20 terabytes of data, not good yet. We know that there is 90 shapes, each shape has exact order of small and big pieces. For example CeCeCeCe/CeCeCeCe is shape of solved cube where 'C' is big and 'e' is small piece. So now we have 90 pieces * 2520 permutations of small pieces * 40320 permutations of big pieces. That is 9144576000 permutation and bytes. I guessed that there is no more than 15 levels (actually only 12), so we could use half byte to store level of any cube. This is 4.2 GB, good enough for my PC!

Now we could for each level go through whole database, and when we found some half-byte which is set to current level, we will do all possible operations on the cube which is described by address of such half-byte. Results will be converted back to address of new cube and written to the database, if new cube is not on same or previous level already. Before saving we should normalize the cube to one of 90 shapes, by rotating top, bottom or flipping whole cube.

Well we could save some work in analysis, we could skip all operations on the shape which will lead to same result for example (0,0)/! is same as (6,6)/ for all shapes. Also we could minimalize those shapes which stay in same shape after rotation, for example (3,6) on solved cube will not change shape, just colors. So we could only write that rotated cube, which has minimal index of color permutation. This will eliminate identical cubes from processing in next level.

Now we need some clever paging because is not possible get it all in memory at once with 2GB address space. There helps that transitions are always between pair of shapes, so we could use file of one shape as source and other as target. Memory mapped file is cool solution for shape files (50MB each). Also helps to lazy flush that files. And lastly try to order processing of page pairs in that order, that just few loading and flushing is performed for each file, because disk is slooow. This could be done because shapes don't produce cubes in all other shapes, just neighbor. Also helps that file is created whole at once to prevent fragmentation on disk. Windows Vista paging seems do better work than XP when working with mapped files. To keep busy processors and disk all the time, we could run more threads than we have processor cores, each thread one pair of shapes. When some thread is waiting for disk, other thread is heating the core.

Now we could optimize the operations on the cube. Here comes the C++/Cli. We generate optimized code branch for expanding cubes, specific for source and target shape. We could also optimize computation of index of permutation, because indexes of 2520 and 40320 permutations could be done by hash table and by lookup table on way back to permutation. And we could also do whole turn (rotation of top bottom, turn and rotation of top and bottom as normalization to the shape) just by using bitwise AND, OR and rotation on processor registers. Such operation could be done separately on permutation of small pieces and big pieces 32 bits each. Also prevent CLR to native and back transitions, so code for whole shape file scan should be native code.

Mike Masonjones did probably something similar in year 2005, maybe he had little bit harder problem, as he was solving cube with 6 colors. He had some old machine which was dedicated to the task and was running a year! My algorithm could do it in cca 8 hours. I have Dual core 2.4GHz + 4GB RAM, but I'm still happy with 1000 times speedup.

I had great time playing with that problem and learned lot. Interesting ? Found bug ? Any feedback ? Enjoy !

Numbers about silver edition

Level 11
Level 232
Level 3536
Level 47652
Level 5102204
Level 61296753
Level 715250126
Level 8153335000
Level 91138303312
Level 104067633460
Level 111434709972
Level 12164952
Total 6810804000 configurations.


Download

Source code under LGPL on SourceForge subversion.
GUI client + database creation engine binaries, .NET 3.0 required. Analysis report
Database of shapes in XML


Cube21 Links


Update: This is probably wrong. My idea was that the correction could be made by merging /(0,6)/(0,6)/(0,6) to last three turns when necessary. I currently don't have time to think about it deeply, but it seems to me now, that it wouldn't work for all shapes. I probably it could mean that my database is not 100% correct and that some paths will be one turn longer. It could also force me to store another bit of information in permutation, so disk space required could be twice bigger. I will welcome anyone who will do the fix :-)