Hatena::ブログ(Diary)

はすけるとぱいそん

2011-11-02

Project Euler 92

問題はこちら

これはかなり前に解いていたのですが、
ちょっと(というかかなりw)遅かったので今回高速化してみました。
案外うまくいきました。僕の低速PCでも3秒くらい。

import Data.List
import Data.Char


-- 辞書順重複組み合わせ。
-- comb [0,1,2] 2 で [[0,0],[0,1],[0,2],[1,1],[1,2],[2,2]]
comb :: [a] -> Int -> [[a]]
comb _ 0 = [[]]
comb [] _ = []
comb (x:xs) n = map (x:) (comb (x:xs) (n-1)) ++ comb xs n


-- 各桁の二乗を足し続けて89ならTrue。
-- f [8,5] で True
f :: [Int] -> Bool
f xs
	| a == 89 = True
	| a == 1 = False
	| otherwise = f b
		where
			a = sum $ map (^2) xs
			b = map (digitToInt) (show a)


-- 階乗。かいじょう!
k :: Int -> Int
k n = product [1..n]


-- リストに一つしか現れない数字を除去し、複数現れる数字をまとめる。
--g [1,1,2,3,3,3,4] で [[1,1],[3,3,3]]
g [] = []
g xs
	| (b == 1) = g (tail xs)
	| otherwise = a : g (drop b xs)
		where
			a = takeWhile (== head xs) xs
			b = length a


-- 普通の順列、あるいは同じものを含む順列の数(ただし先頭に0を含まない)。
-- j [1,2,2,3,3,3] で 60。つまり6!/(2!*3!)を計算してる。
j :: [Int] -> Int
j xs = div (k (length xs)) (product $ map (k) $ map (length) $ g xs)


-- 0を含む時の順列の数!!!(先頭に0がきてはいけない)
-- jj [0,1,2,3] で 18。この場合3*3!を計算してる。
jj :: [Int] -> Int
jj xs = sum [ j (delete x xs) | x <- tail (nub xs) ]


-- リストの先頭に0がある時は関数jj、ないなら関数jで。
h :: [Int] -> Int
h xs
	| (head xs == 0) = jj xs
	| otherwise = j xs


-- 1から9999999の数字を桁の重複組み合わせで効率良く出す。これだと19440個だけ調べれば良い。
-- [[1],[2],・・・[0,4,5],・・・[1,3,3,4,5],[9,9,9,9,9]・・・]
a :: [[Int]]
a = concat [ tail $ comb [0..9] x | x <- [1..7] ]


--こたえだよ!おにいちゃん!
main = print $ sum $ map h $ filter f a

今回のコードはそこまで圧倒的にくそくないです。たぶん。
 
 

Project Euler 54

問題はこちら

バグ取りによって一時死去したけれど、復活して完成。とりあえずうp。
変にまわりくどいし、Haskellなのに超長いし(他の人々のと比較して2〜4倍長い)、
しかも型書いてないし(ぉぃ、とんでもなく圧倒的にくそいよお。
disらないでください。すいませんorz

本当にくそいです。

import Data.List
import Data.Char
import Data.Maybe


-- データ構造 ([カードの数値],"スート")。
ehuda = zip ['T','J','Q','K','A'] [10,11,12,13,14]

f s
	| isAlpha a = fromJust (lookup a ehuda)
	| otherwise = read (take 1 s)::Int
		where a = head s

g ss = (reverse $ sort $ map f ss , map last ss)



-- ポーカーの役判定。cardの型は([カードの数字],"スート")。

-- リストの要素が全て同じならTrue。
same xs
	| (length xs < 2) = True
	| (head xs == xs !! 1) = same (tail xs)
	| otherwise = False

-- nカードなら(True,[役の数字部分],[数字全体])。以下全てこの型。
nCard (xs,ys) z n
	| (length xs < n) = (False,[],[])
	| same t = (True,t,z)
	| otherwise = nCard (tail xs,ys) z n
		where t = take n xs

-- ワンペア。
onePair card = nCard card (fst card) 2

-- ツーペア(なんかあれw)。
twoPair' card z
	| same a && same b = (True,a++b,z)
	| same a && same c = (True,a++c,z)
	| same d && same e = (True,d++e,z)
	| otherwise = (False,[],[])
		where
			f = fst card
			a = take 2 f
			b = take 2 $ drop 2 f
			c = drop 3 f
			d = take 2 $ tail f
			e = drop 3 f
twoPair card = twoPair' card (fst card)

-- スリーカード。
threeCard card = nCard card (fst card) 3

-- ストレート。
stright card
	| f == (reverse $ take 5 $ iterate (+1) (last f)) = (True,f,f)
	| otherwise = (False,[],[])
		where f = fst card

-- フラッシュ。
flash card
	| same (snd card) = (True,f,f)
	| otherwise = (False,[],[])
		where f = fst card

--フルハウス。
hullHouse' card z
	| same a && same b = (True,a++b,z)
	| same c && same d = (True,c++d,z)
	| otherwise = (False,[],[])
		where
			f = fst card
			a = take 3 f
			b = drop 3 f
			c = take 2 f
			d = drop 2 f
hullHouse card = hullHouse' card (fst card)

-- フォーカード。
fourCard card = nCard card (fst card) 4

-- タプルの第1要素と第2第3要素を返す。
fst3 (a,b,c) = a
snth3 (a,b,c) = (b,c)

-- ストレートフラッシュ。
strightFlash card
	| (fst3 (stright card) && fst3 (flash card)) = (True,f,f)
	| otherwise = (False,[],[])
		where f = fst card

-- ロイヤルストレートフラッシュ。
loyalStrateFlash card
	| (fst card == [10..14] && fst3 (flash card)) = (True,[10..14],[10..14])
	| otherwise = (False,[],[])

-- カードの役の(得点,([役の数字部分],[数字全体]))
poker card
	| fst3 (loyalStrateFlash card) = (9,snth3 (loyalStrateFlash card))
	| fst3 (strightFlash card) = (8,snth3 (strightFlash card))
	| fst3 (fourCard card) = (7,snth3 (fourCard card))
	| fst3 (hullHouse card) = (6,snth3 (hullHouse card))
	| fst3 (flash card) = (5,snth3 (flash card))
	| fst3 (stright card) = (4,snth3 (stright card))
	| fst3 (threeCard card) = (3,snth3 (threeCard card))
	| fst3 (twoPair card) = (2,snth3 (twoPair card))
	| fst3 (onePair card) = (1,snth3 (onePair card))
	| otherwise = (0,(fst card,fst card))

-- ポーカーのルール。
pokerRule [s1,s2]
	| (point1 > point2) = 1
	| (point1 == point2) && (a > c) = 1
	| (point1 == point2) && (a == c) && (b > d) = 1
	| otherwise = 0
		where [(point1,(a,b)),(point2,(c,d))] = map poker [s1,s2]

-- 二人でのポーカープレイ。
pokerPlay [] = 0
pokerPlay ss = pokerRule (take 2 ss) + pokerPlay (drop 2 ss)



-- テキストファイルからデータ取得してポーカープレイ。
ff [] = []
ff s = (take 2 s) : ff (drop 3 s)

gg [] = []
gg s = (take 5 s) : gg (drop 5 s)

main = do
	   a <- readFile "poker.txt"
	   print $ pokerPlay $ map g $ (gg.ff) a


大事なことなのでもう1度言いますが、本当に本当に、くそいです。