Hatena::Grouphaskell

Haskell卒業!

  Haskellの勉強 -> 演習 -> 卒業
  Haskell&プログラミング卒業しました。その他サイコなことは「route150の日記」に書いています。

2010年09月30日 木曜日

[]TWOSQRS(2) 13:10

結局、又C/C++で解いた...orz


なんか知らないけど同じコードを実行してもC++の方が2倍くらい速い...


ただ、実行時間的にもっと良いやりかたがあるようだ...


フォーラムのコードそのまんまなんだけど...


#ifdef WIN32
#define INPUT "%I64u"
#define _CRT_SECURE_NO_WARNINGS
#else
#define INPUT "%lld"
#endif

#include <cstdio>

#define N 1000000
typedef unsigned long long ull_t;
int sieve[N];
ull_t primes[100000];

int main(int argc, char* argv[])
{
    ull_t n;
    int T, i, j;
    int index = 0;

    for(i = 2; i < N; i++)
        if(!sieve[i]){
            primes[index++] = i;

            for(j = i * 2; j < N; j += i)
                sieve[j] = 1;
        }

        scanf("%i", &T);

        while(T--){
            int done = 0;

            scanf(INPUT, &n);

            if(n > 0){
                for(i = 0; i < index; i++){
                    int count = 0;

                    while(n % primes[i] == 0){
                        if(primes[i] % 4 == 3)
                            count++;

                        n /= primes[i];
                    }

                    if(count % 2 != 0){
                        printf("No\n");
                        done = 1;
                        break;
                    }
                }
            }

            if(!done)
                puts(n % 4 != 3 ? "Yes" : "No");
        }

        return 0;
}

[]TWOSQRS(3) 15:54

Haskellでやってみたけど、素数生成でTLEしてしまう。比較的速いData.Seive.ONeillのコードを貼り付けたり、STでエラトステネスの篩を書いたけど、素数生成で精一杯で、その後の80000弱 * 100の走査をする時間がない。出力だって遅いのに勘弁...


他の方法で尺取りメソッドというのを使うと解けるらしいとのことだったのでやってみた。尺取りメソッドとは...、なんか検索しても見つからない???


せめて英語でなんて呼ぶかわかれば情報収集できそうだが、それすらわからない。が、方向性はわかったので、なんとか素数使わずにC/C++ならACするコードが書けた。


で、それをHaskellに移植、結果はTLEだった。untilとか使ってるので、ここをベタに書けば通るかもしれないし、x86コードにして埋め込めば確実にパスするけど、もう嫌になった。C/C++で0.8秒くらい掛かってるから、0.1秒くらいで解くようなアルゴリズムでないとHaskellでは厳しい...


多分、こんな感じでしょう...


#ifdef WIN32
#define INPUT "%I64u"
#define _CRT_SECURE_NO_WARNINGS
#else
#define INPUT "%lld"
#endif

#include <cstdio>
#include <cmath>

typedef unsigned long long ull_t;

bool check(ull_t n){
    ull_t x(0), y(static_cast<ull_t>(floor(sqrt(static_cast<double>(n)))));

    for(;; x++){
        while(x * x + y * y > n)
            --y;
        while(x * x + y * y < n)
            ++x;

        if(y < x)
            break;

        if(x * x + y * y == n)
            return true;
    }

    return false;
}

int main(int argc, char* argv[])
{
    ull_t n;
    int T;

    scanf("%i", &T);

    while(T-- > 0){
        int done = 0;

        scanf(INPUT, &n);

        printf("%s\n", check(n) ? "Yes" : "No");
    }

    return 0;
}

Haskell...


なんかuntil使ってる辺りでTLEの臭いがプンプンする...


{-# LANGUAGE BangPatterns #-}

import Control.Applicative
import Control.Monad
import Data.Int

check :: Int64 -> String
check n = calc 0 . floor . sqrt . fromIntegral $ n where
  calc !x !y = do
    let y' = until ((<= n) . (+ x ^ 2) . (^ 2)) (subtract 1) y
        x' = until ((>= n) . (+ y'^ 2) . (^ 2)) (+ 1)        x
    case () of
      _ | y' < x'              -> "No"
        | x' ^ 2 + y' ^ 2 == n -> "Yes"
        | otherwise            -> calc (x' + 1) y'

main :: IO ()
main = do
  loop <- replicateM_ <$> readLn
  loop $ readLn >>= putStrLn . check

[]STAMPS 16:47

入力がでかいとビビルけど、この問題は大丈夫...だった...


つか、問題になってない...


{-# LANGUAGE OverloadedStrings #-}

import Control.Applicative
import Control.Monad
import Data.List
import Data.Maybe
import qualified Data.ByteString.Char8 as BS

main :: IO ()
main = do
  let readInts :: IO [Int]
      readInts = map (fst . fromJust . BS.readInt) . BS.words <$> BS.getLine
  t <- readLn
  forM_ [1 .. t] $ \caseNo -> do
    stamps : n : _ <- readInts
    friends <- scanl1 (+) . sortBy (flip compare)
             . take n <$> readInts
    let answer = case findIndices (stamps <=) friends of
          x : _ -> BS.pack . show $ x + 1
          _     -> "impossible"
    BS.putStrLn $ BS.concat ["Scenario #", BS.pack $ show caseNo,":"]
    BS.putStrLn $ answer
    BS.putStrLn ""

[](演習)Haskellで正規表現を使いたい...orz(1) 21:04

Haskellで正規表現を使いたい。高機能じゃなくていいから、サクっと使える奴を作りたい。HackageDBに行けば、Regex.PosixとかRegex.TDFAとかあるけど、OJ(Online Judge)では使えないので貼り付けることができるような奴が欲しい...


低機能でも汎用性が欲しいので、Parsecとか標準かどうか微妙なライブラリは使わずに書く。出力としては、マッチした文字列の開始と終了インデックスを全てリストで返すことができれば十分とする。OJで使うかもしれないので、NFA(非決定性有限オートマトン(Non-Deterministic-Finite-Automaton))エンジンではなく、DFA(決定性...(Deterministic...))エンジンの奴でないと意味ない。NFAだとTLE確実だから...


Stringで作る方がパターンマッチとか使えてHaskellらしく書けると思うけど、遅すぎては意味がないのでByteStringを使う...


とりあえず、構文木を作る → NFAに変換 → DFAに変換の順で。参考書は「Cプログラマのためのアルゴリズムとデータ構造((著)近藤嘉雪)」位しか見当たらないないのだが、Haskellでも多分組めるでしょう...組めると思いたい...


まず、pureな正規表現(|、*、連結)が正しく処理できるようにする。(+、.、!、文字クラス、{n,m})とかは、前者を組み合わせるだけなので後で。入力はASCII限定でおk。エンコーディングとか考える必要なし(DFAなので2バイト文字は状態的に扱いたくない)...


要件はこれくらいかなぁ...


ただ、すごく長くなりそう...

[][](演習)Haskellで正規表現を使いたい...できるかなぁ(2) 21:23

とりあえず、入力を1文字ずつ読み込んで構文木を構築するパーザを作る。と言っても、Haskell的なやり方が全くわからないのだが、C/C++再帰降下する感じでやるつもり。Parsecがこの辺は得意なんだけどなぁ...


で、入力を処理していって、成功ならJust(構文木)、途中で失敗したらNothingを返すようにしたいのだが、普通に関数で書こうとするとメンドイ...


だから、モナドにして処理の面倒な部分は(>>=)に押し込む。


それってStateTのベースモナドをMaybeにすればできなくね???


→ 書き終えてから気が付いた...orz。でも、failとか独自に定義したいかもしれないから、まぁいいや...


{-# LANGUAGE OverloadedStrings, ScopedTypeVariables, TypeSynonymInstances #-}

import Control.Applicative
import Data.List
import qualified Data.ByteString.Char8 as BS
import Test.QuickCheck
import Test.QuickCheck.Checkers
import Test.QuickCheck.Classes
import Data.Typeable

type BString = BS.ByteString
newtype TreeBuilder a = TreeBuilder (BString -> Maybe (BString, a))

instance Monad TreeBuilder where
  return x = TreeBuilder $ \s -> Just (s, x)
  (TreeBuilder f) >>= k = TreeBuilder $ \s -> do
    let next (s', x') = g s' where
           TreeBuilder g = k x'
    f s >>= next
  fail _ = TreeBuilder $ \_ -> Nothing

runTreeBuilder :: TreeBuilder a -> BString -> Maybe a
runTreeBuilder (TreeBuilder f) pattern = do
  snd <$> f pattern

getToken :: TreeBuilder Char
getToken = TreeBuilder $ \s -> do
  if BS.null s
    then Nothing
    else Just (BS.drop 1 s, BS.head s)

IOやSTみたいに怪しいことするわけじゃなく、ただの関数合成を書きやすくしてるだけだからモナド則は基本的に成り立つはずだが、一応チェックする...


instance forall a. (Typeable a) => Show (TreeBuilder a) where
  show _ = "show: TreeBuilder - " ++ show (typeOf (undefined :: a))

instance Arbitrary BString where
  arbitrary = BS.pack <$> arbitrary

instance CoArbitrary BString where
  coarbitrary s = coarbitrary $ BS.unpack s

instance (Arbitrary a) => Arbitrary (TreeBuilder a) where
  arbitrary = TreeBuilder <$> promote (`coarbitrary` arbitrary)

instance EqProp a => EqProp (TreeBuilder a) where
  (TreeBuilder f) =-= (TreeBuilder g) = property $ (=-=) <$> f <*> g

instance EqProp BString where
  a =-= b = property $ a == b

checkMonadLaws :: IO ()
checkMonadLaws = mapM_ quickBatch $
  [ monad (undefined :: TreeBuilder (Int, Int, Int))
  , monad (undefined :: TreeBuilder (String, Maybe Int, Int))
  , monad (undefined :: TreeBuilder (Char, [Int], (Int, Char)))]

-- $> checkMonadLaws
-- monad laws:
--   left  identity: +++ OK, passed 500 tests.
--   right identity: +++ OK, passed 500 tests.
--   associativity:  +++ OK, passed 500 tests.
-- monad laws:
--   left  identity: +++ OK, passed 500 tests.
--   right identity: +++ OK, passed 500 tests.
--   associativity:  +++ OK, passed 500 tests.
-- monad laws:
--   left  identity: +++ OK, passed 500 tests.
--   right identity: +++ OK, passed 500 tests.
--   associativity:  +++ OK, passed 500 tests.
-- (18.12 secs, 5209363436 bytes)

おkです...


これでdo記法を使ってコードが書ける。pureな正規表現は先読みする必要ない(と思う)から、多分コレで十分...


parse :: BString -> Maybe Bool
parse s = (`runTreeBuilder` s) $ do
  c1 <- getToken
  c2 <- getToken
  return $ c1 == 'A' && c2 == 'B'

test :: IO ()
test = do
  print $ parse "abcde"
  print $ parse "ABCDE"
  print $ parse ""

-- $> test
-- Just False
-- Just True
-- Nothing
-- (0.00 secs, 0 bytes)

こういうコードはHaskellは異様に書きやすい...


続くかもしれないし、心が折れるかもしれない...