読者です 読者をやめる 読者になる 読者になる

和歌山産pythonプログラマのブログ

和歌山出身プログラマmatsu7874が書いています。Python3と時々C++11を書きます。

そのミスはどう処理しましょうか?

ボードゲーム Advent Calendar

この記事は、Board Game Design Advent Calendar 2015の10日目の記事ですが、26日目に書かれました。

僕は週に8時間ほどボードゲームで遊びます。 ボードゲームで遊ぶのは楽しいです。 もう一回もう一回と夜通し遊んでしまうことも多々あります。

遊ぶだけの側からボードゲームに思うこと、楽しいボードゲームライフの敵、ミスについて書きます。 人間はミスをします。徹夜で遊んだりなんかするとミスの頻度も上がります。

この記事で伝えたいこと

うっかり操作を間違えても楽しく遊べるゲームが遊びたいです。 できれば操作を間違えないゲームを遊びたいです。 この記事はゲームに文句をいいますが、登場するゲームはどれも超面白いという前提でお読みください。

遊びやすさに関わるプレイングミス

基本的に遊びにくいだけなので、相手と競い合う面白さの部分には影響がないように思う。
遊びやすいと「これ便利でいいね」「これ分かりやすいね」などといいながら遊ぶことはある。

  • 山を崩す(この町は2-4人のボクらにも狭すぎる)
    • 積んだタイルの一番上からめくるときに山を崩しがち。
    • そもそも積む必要が無い。裏面は全て同じ模様なので、適当に平たく置いている中から1枚取ればいい。
  • カードがめくり辛い(ごきぶりポーカー)
    • 地面に何か敷け(遊ぶ側の問題)
    • カードがめくりにくくて曲げ後や爪の跡などが残ってしまうとゲーム上問題
  • コマをつまみづらい(エクリプスpdweb.jp プロダクトデザインの総合Webマガジン)
    • 小さいコマをマスの上に並べた上で取り除くことによって経済力などを表す
    • 他の列を触らずにコマを取るのが結構難しい
    • 6時間とかかかるゲームなので優しくない(コンピュータに管理して欲しい)

ゲームの面白さに関わるプレイングミス

秘密情報を公開してしまう

秘密情報がゲームのキモで読み合いなどを楽しむゲームの場合、ゲームの面白い部分が消えてしまう。

  • コマを倒してしまう(ガイスター)
    • 倒さないように気をつける
  • 手札を見せてしまう(ごきぶりポーカー)
    • 手札を傾けないように気をつける
  • 引いたカードの内容が見えてしまう(ラブレター)
    • 引いたカードをすぐに開けずに自分の手元まで持ってきてから見る。

秘密情報を不当に知ってしまう

一方的にミスをした人が情報上有利になってしまう。

  • タイルを引きすぎる(エクリプス)(これは接戦の時のみ重要)
    • 1から4の勝利点が書いてあり数枚引いて一番数字が大きい物を取って残りを戻す処理を行う。
    • 取ったタイルは非公開情報なので決着が着くまで相手のタイルの点数が分からない。
    • それぞれの枚数がわかっているので手元に4が何枚かあり、まだ引かれていない4が確認できれば、相手のタイルが4でないことが確認できる。
    • 一枚ずつ確認してから引くことで解決?
  • カードを引きすぎる(ドミニオンなど)
    • 山のカードの情報でプレイを変えることが可能になってしまう。

状況を曖昧にしてしまう

お互いに曖昧な中で自分に有利な所に再設定したがるので質が悪い。
釈然とはしないものの競い合う面白さはギリギリのところで戦っていないかぎり壊れない。

  • 未使用のサイコロを振ってしまう(ドラスレ)

    • サイコロをいくつか振って移動に使うが、6の目がでると割り込み処理が入る
    • 割り込み処理で全員がサイコロを振ることがあり、うっかり手近にあるこのターン移動に使うべきサイコロを振ってしまう。
    • 各自用にサイコロを用意することで解決か?
  • 資源マーカーをずらしてしまう(エクリプス)

    • 相手の資源の量なんて覚えていない可能性が高いので困る。
    • 下手すると自分でもなんとなくでしか覚えていないので困る。
    • お互いに曖昧な範囲内で自分に有利な所に設定したがるから質が悪い。
  • 資源を取ったか忘れてしまう(カタン)
    • だいたい上と同じ
    • 最大手札枚数が設定されているのでなんとなく枚数を覚えることが出来る量に収まっている。
  • 誰のターンか忘れてしまう(さまざまなゲーム)
    • マーカーとかチェスクロックがあるといい。(渡し忘れや押し忘れの可能性はある。)
  • 計算を間違える(エクリプス)
    • 相手に確認しながら操作する

処理の順番を間違える

  • 手札を1枚捨ててから2枚引くのか、2枚引いてから1枚捨てるのか、任意の順番で行っていいのか
    • ゲームが始まってしまっては解釈によって有利不利が出てしまう。
    • ルールブックの Q&A などに書いてくれると目につきやすい
    • ゲームが始まる前に確認しておきたい
  • 効果発生のタイミングを勘違いする
    • カードAの効果がカードA自体にも発動するか?
  • カードを引き忘れる(俺の街 ~Ore City~(リニューアル版) : 電源不要ゲーム製作サークル トーホクウィステリア)
    • 手札が常に3枚になるルールのおかげで引いていないことに気付きやすい
    • 次の人の行動を見てから行動を変えられる(4種類の山から引くカードを選べ、山ごとにカードの種類が違う)
    • 山札が無くなって捨て札がシャッフルして山札に補充されると、同じ種類の山でも内容が激変する

解決策っぽいもの

その他

  • ミスでゲームが壊れてしまっても、プレイ時間が短ければもう一度遊ぶかということになりやすい。
  • ミスを解決する方法でおすすめの方法があれば、(ルールの余白があれば)それもルールとして決めて欲しい。
  • ミスを未然に防ぐデザインが最強

今後もミスに負けない面白いゲームを作ってください。

シフトを自動で組むプログラムをPythonで書いた

Python

アルバイトのシフトを組むのって大変らしいですね。

調べてみると「ナーススケジューリング問題はNP困難」などと出てきます。

ただ、頭のなかで考えていた問題は労働者ごとの職能に差がないものだったので、ナーススケジューリング問題よりは簡単かもしれない。……どうせ人間が目視で確認するし、何パターンか出力すれば良くね? 表にする部分の作業をやるだけでも楽になるのでは?と思い、最適化とか考えずに可能なシフトをいくつか出力してみました。

問題設定

入力

  • 労働者の希望勤務枠が与えられる。労働者が希望していない労働枠での勤務は不可。
  • 労働枠に対して必要人数が与えられる。必要人数以上の勤務は不可。不足の場合はダミー労働者を配置する。
  • 労働枠の重なりが与えられる。重なりのある労働枠を同一労働者が勤務することは不可。

出力

  • シフト案100個
  • 各シフト案に対して
    • 勤務枠ごとの勤務労働者
    • (シフト案を評価する目安として)勤務労働者内における勤務回数の標準偏差
import copy
import random
import collections
import string


def is_legal(plan, required, possible, group):
    for cell, workers in plan.items():
        if len(workers) != required[cell]:
            return False
        for worker in workers:
            if cell not in possible[worker]:
                return False
    for g in group:
        worked = []
        for cell in g:
            for worker in plan[cell]:
                if worker in worked:
                    return False
                worked.append(worker)
    return True


def evaluate(plan):
    total_work = 0
    worked = {}
    for cell, workers in plan.items():
        total_work += len(workers)
        for worker in workers:
            if 'dummy' in worker:
                pass
            elif worker in worked:
                worked[worker] += 1
            else:
                worked[worker] = 1
    total_work /= len(worked)
    score = sum(v * v for k, v in worked.items()) / len(worked)
    if score - total_work**2 < 0:
        return total_work + (total_work**2 - score)**0.5
    return (score - total_work * total_work)**0.5


def solver(required, possible, group):
    cells = [k for k, v in required.items()]
    workers = [k for k, v in possible.items()]
    timetable = {}
    for cell in cells:
        timetable[cell] = []
        for worker in workers:
            if cell in possible[worker]:
                timetable[cell].append(worker)
    generation = 0
    plan = {cell: [] for cell in cells}
    plans = list()
    for i in range(100):
        if is_legal(plan, required, possible, group):
            for cell in cells:
                plan[cell].sort()
            score = evaluate(plan)
            plans.append(
                (score, str(list(sorted(plan.items(), key=lambda t: t[0])))))
        for cell in cells:
            while len(timetable[cell]) < required[cell]:
                suffix = random.sample(string.ascii_uppercase, 4)
                dummy = 'dummy' + ''.join(suffix)
                timetable[cell].append(dummy)
                if dummy not in possible:
                    possible[dummy] = []
                possible[dummy].append(cell)
            plan[cell] = random.sample(timetable[cell], required[cell])

    return list(plans)


def generate_sampledate():
    eachday = {'A': 2, 'B': 1, 'C': 2, 'D': 1, 'E': 3, 'F': 1}
    not_same_member = [('A', 'B'), ('C', 'D'), ('E', 'F')]
    required = {}
    group = []
    for day in range(1, 31):
        d = ('0' + str(day))[-2:]
        for k, v in eachday.items():
            required[d + k] = v
        for g in not_same_member:
            group.append((d + e for e in g))

    workers = ['Amy', 'Basil', 'Clara', 'Desmond', 'Ernest', 'Fanny', 'George',
               'Hector', 'Ida', 'James', 'Kate', 'Leo', 'Maud', 'Neville',
               'Olive', 'Prue', 'Quentin', 'Rhoda', 'Susan', 'Titus', 'Una',
               'Victor', 'Winnie', 'Xerxes', 'Yorick', 'Zillah']
    possible = {}
    for w in workers:
        possible[w] = []
        for k in required:
            if random.random() > 0.78:
                possible[w].append(k)
    return required, possible, group


if __name__ == '__main__':
    plans = solver(*generate_sampledate())
    plans.sort()
    print(*plans, sep='\n')

感想

paizaのプログラミングで彼女を作るやつを全クリアした

Python

paiza.jp 彼女欲しいって言ってたら友達にオヌヌメされたので解いた。全体的に制約が超ゆるいので何でも通っちゃう。 一応水着も手に入れたけど、僕がほしいのは生身の彼女なんだ!

「めがね」ゲットチャレンジ!

本当にやるだけ。制約が小さいので全部見れる。

N=int(input())
Q=[list(map(int, input().split())) for i in range(N)]
M=int(input())
P=[list(map(int, input().split())) for i in range(M)]
for y in range(N-M+1):
    for x in range(N-M+1):
        is_miss = False
        for i in range(M):
            for j in range(M):
                if Q[y+i][x+j] != P[i][j]:
                    is_miss = True
                    break
            if is_miss:
                break
        if not is_miss:
            print(y,x)
            exit()

「サンタ服」ゲットチャレンジ!

個人的にサンタ服は好きです。何も考えずにXとYの最小幅とZをかける。

X, Y, Z, N = map(int, input().split())
cut = [[0, X], [0, Y]]
for i in range(N):
    d, a = map(int, input().split())
    cut[d].append(a)
min_v = [X, Y]
for i in range(2):
    cut[i].sort()
    for j in range(1, len(cut[i])):
        min_v[i] = min(min_v[i], cut[i][j] - cut[i][j - 1])
print(min_v[0] * min_v[1] * Z)

「水着」ゲットチャレンジ!

1<=N<=106が与えられるのでN! の最下位桁から続く0 をすべて除いた値の下位9桁を求める問題。
毎回109乗で余りを取っていけばいいかと思うのだが、5の2乗以上をかけた時にゼロが下に複数個ついて必要な桁の情報を持っていないようだったので59-106なので8桁余分に持って計算を進める。

N = int(input())
t = N
for i in range(2, N):
    t *= i
    while t % 10 == 0:
        t //= 10
    t %= 10**(9+8)
print(t % (10**9))
続きを読む

普段競技プログラミングばっかりやってる僕がSiv3Dを触ってみた話

Siv3D C++

この記事はSiv3D Advent Calendar 2015 - Qiitaの14日目の記事です。


普段はプログラミングといえば競技プログラミングというレベルで競技プログラミング大好きな僕のSiv3D体験記です。

Siv3Dとは

Siv3D は C++ で楽しく簡単にゲームやメディアアートを作れるライブラリ

play-siv3d.hateblo.jp

インストール

まずはインストールから

  1. Siv3Dを使うにはVisual Studio 2013が必要らしい。
  2. Visual Studio 2013をインストール(途中で言語パックがおかしいと言われ4時間位書けて完了)
  3. Siv3Dにはインストーラー(Siv3D_Install.wsf)が付いている!
  4. Siv3D_Install.wsfをダブルクリックすると"Visual Studio 2013 の Project Template フォルダが見つかりませんでした。手動でのセットアップを試してください。"と言われる。
  5. 絶望
  6. Siv3D_Install.wsfの中を読む。->"Visual Studio 2013Templates\ProjectTemplates"にzipをコピーすればええんちゃう?
  7. 動く

円とか線とか

Siv3DはすごいのでCircle().draw()とか書くだけで円が表示できちゃいます。 Siv3DはすごいのでLine().draw()とか書くだけで線が表示できちゃいます。 Siv3DはすごいのでCircle().leftClickedとか書くだけで領域が左クリックされたか判定できちゃいます。 Siv3Dはすごすぎるので今回はこの3つをなんとなくごちゃごちゃしてるうちに日をまたぎこんなのが出来上がりました。 f:id:matsu7874:20151215022649p:plain

頂点数120のグラフを表示し、右クリックで始点を、左クリックで終点を選択して、最短経路を赤く表示するコードです。 ダイクストラで距離を求めて、始点から到達できない頂点は緑色に表示します。2000頂点くらいまでならさくさく動きます。

#define N_NODE 120

# include <Siv3D.hpp>
# include <vector>

struct Node{
    int x, y;
    Color color;
    std::vector<int> to;
    std::vector<double> cost;
    bool visited;
    double minCost;
    int from;
};

void dijkstra(int n, int start, struct Node *node){
    for (int i = 0; i < n; i++){
        node[i].visited = false;
        node[i].minCost = -1;
        node[i].from = -1;
    }
    node[start].minCost = 0;
    node[start].from = start;
    while (1){
        int visitedNode = -1;
        for (int i = 0; i < n; i++){
            if (node[i].visited == true){
                continue;
            }
            if (node[i].minCost < 0){
                continue;
            }
            if (visitedNode < 0 || node[i].minCost < node[visitedNode].minCost){
                visitedNode = i;
            }
        }

        if (visitedNode == -1){
            break;
        }

        node[visitedNode].visited = true;

        for (unsigned int i = 0; i < node[visitedNode].to.size(); i++){
            int to = node[visitedNode].to[i];
            double cost = node[visitedNode].minCost + node[visitedNode].cost[i];
            if (node[to].minCost < 0 || cost < node[to].minCost){
                node[to].minCost = cost;
                node[to].from = visitedNode;
            }
        }
    }
}

std::vector<int> CreatePath(int start, int end, struct Node *node){
    std::vector<int> path;
    if (node[end].minCost > 0){
        for (int i = end; i != start; i = node[i].from){
            if (i < 0){
                return std::vector<int>{start};
            }
            path.push_back(i);
        }
        path.push_back(start);
    }
    else{
        path.push_back(start);
    }
    return path;
}

void Main()
{
    const Font font(20);
    struct Node node[N_NODE];
    for (int i = 0; i < N_NODE; ++i){
        node[i].x = Random(10, 620);
        node[i].y = Random(40, 460);
        node[i].color = Color({ 255, 255, 255 });
    }
    for (int i = 0; i < N_NODE + 10; i++){
        int a = Random(0, N_NODE - 1);
        int b = Random(0, N_NODE - 1);
        while (a == b){
            b = Random(0, N_NODE - 1);
        }
        node[b].to.push_back(a);
        node[a].to.push_back(b);
        double dist = (node[a].x - node[b].x)*(node[a].x - node[b].x);
        dist += (node[a].y - node[b].y)*(node[a].y - node[b].y);
        node[a].cost.push_back(Sqrt(dist));
        node[b].cost.push_back(Sqrt(dist));
    }
    int root = 0;
    int target = 1;
    dijkstra(N_NODE, root, node);
    std::vector<int> path = { 0 };
    node[root].color = Palette::Blue;
    node[target].color = Palette::Red;
    while (System::Update())
    {
        for (int i = 0; i < N_NODE; ++i){
            if (Circle(node[i].x, node[i].y, 2).leftClicked){
                node[target].color = Palette::White;
                target = i;
                node[target].color = Palette::Red;
                path = CreatePath(root, target, node);
            }
            else if (Circle(node[i].x, node[i].y, 2).rightClicked){
                node[root].color = Palette::White;
                root = i;
                node[root].color = Palette::Blue;
                dijkstra(N_NODE, root, node);
                path = CreatePath(root, target, node);
            }
        }
        for (int i = 0; i < N_NODE; ++i){
            for (int j = 0; j < node[i].to.size(); ++j){
                Line(node[i].x, node[i].y, node[node[i].to[j]].x, node[node[i].to[j]].y).draw(Palette::White);
            }
        }
        for (int i = 0; i < path.size() - 1; ++i){
            Line(node[path[i]].x, node[path[i]].y, node[path[i + 1]].x, node[path[i + 1]].y).draw(2.0, Palette::Red);
        }
        for (int i = 0; i < N_NODE; ++i){
            if (node[i].from == -1){
                Circle(node[i].x, node[i].y, 3).draw(Palette::Green);
                if (i == target){
                    Circle(node[i].x, node[i].y, 2).draw(node[i].color);
                }
            }
            else{
                Circle(node[i].x, node[i].y, 2).draw(node[i].color);
            }
        }
        font(node[target].minCost).draw(0, 0, Palette::Gray);
    }
}

ハマった所

  • 関数なのか変数なのかCircle(node[i].x, node[i].y, 2).leftClickedとか関数っぽくないですか?変数です。
  • アイテムが重なっている場合、画面に表示されるのは一番最後に描写したアイテムです。大事なものから描きたいやんけ!慣れない。

疑問点

  • 辺描いて、特殊な辺を上書きして、頂点描いてとループを3つに分けているのがキモくないだろうか。
  • アルゴリズム部分の状態管理と画面への表示状態を上手く管理する方法。

結論

Siv3Dはやばい。C++の練習のためにももっと書き込まなば。学ばねば。

SymPyで辛い計算をちょっと辛い計算にする

Python

この記事はPython Advent Calendar 2015 - Adventarの7日目の記事です。


SymPyというシンボル計算ライブラリを使ったことがありますか? 最近、卒論のお手伝いをしてもらっているライブラリです。 日本語の情報が少なくて困ったので自分用にという意味も込めて使い方を書きます。

インストール

Anacondaを使います。大人しくAnacondaを使います。

シンボル計算ライブラリ SymPy

シンボル計算ライブラリというのは「X」とか「Y」とか多項式の計算をしてくれるライブラリです。 例えば{Z = X \left(2 X + 4\right) + 5 Y - 7}なんて式が与えられたとして {Z^{2}}を計算する必要があるとしましょう。 {Z^{2} = \left(X \left(2 X + 4\right) + 5 Y - 7\right)^{2} = 4 X^{4} + 16 X^{3} + 20 X^{2} Y - 12 X^{2} + 40 X Y - 56 X + 25 Y^{2} - 70 Y + 49}となるのですが、こんな計算は高校の宿題までにしたいところで、SymPyの出番になるわけです。

使う記号を宣言して、計算式を入力するだけで計算結果を表示してくれるなんて、なんて素敵なんだ!

import sympy

sympy.init_printing()
# いい感じに出力するよう設定してくれるらしい

X = sympy.Symbol('X')
Y = sympy.Symbol('Y')
# 使用する記号を宣言して、変数に代入
# 左辺の変数Xと右辺の引数の文字Xは異なる文字列でも可
# 混乱を防ぐために同じ文字を使っている。

Z = 2 * (X + 2) * X + 5 * Y - 7

print(Z)
# => X*(2*X + 4) + 5*Y - 7

ZZ = Z * Z
print(ZZ)
# => (X*(2*X + 4) + 5*Y - 7)**2
print(sympy.simplify(ZZ))
# => (X*(2*X + 4) + 5*Y - 7)**2
print(sympy.expand(ZZ))
# => 4 X^{4} + 16 X^{3} + 20 X^{2} Y - 12 X^{2} + 40 X Y - 56 X + 25 Y^{2} - 70 Y + 49

代入 subs

多項式を計算したら、数値を代入したくなるのが人情というもの。 substituteのsubsだと思われるsubs関数を使って値を知ることが出来ます。 引数はタプルのリストで与え、各タプルの第一要素は何「に」代入するか、第二要素は何「を」代入するかを与えます。

print(Z.subs([(X, 1), (Y, 2)]))
# => 9
print(ZZ.subs([(X, 1), (Y, 2)]))
# => 81
print(ZZ.subs([(Z, X)]))
# => X**2

latex

さらに便利なのがsympy.latexという関数。この子を使えば添字も分数もバシッと出力してくれます。

print(sympy.latex(Z))
# => X \left(2 X + 4\right) + 5 Y - 7

print(sympy.latex(sympy.expand(ZZ)))
# => 4 X^{4} + 16 X^{3} + 20 X^{2} Y - 12 X^{2} + 40 X Y - 56 X + 25 Y^{2} - 70 Y + 49

print(sympy.latex(ZZ/(Z+1)))
# => \frac{\left(X \left(2 X + 4\right) + 5 Y - 7\right)^{2}}{X \left(2 X + 4\right) + 5 Y - 6}

手打ちしたくない数式も出力してくれます。 {\frac{Z^{2}}{Z+1} = \frac{\left(X \left(2 X + 4\right) + 5 Y - 7\right)^{2}}{X \left(2 X + 4\right) + 5 Y - 6}}

latexのバグ探しに時間が溶かされなくていいですね。 以上SymPyの話でした。

Brainfuckの処理を書いてみた

Brainfuckとは

Brainfuck - Wikipedia

命令が8個(><+-.,[])しかないプログラミング言語

def brainfuck(s):
    a = [0] * 10
    ptr = 0
    i = 0
    l = len(s)
    while i < l:
        if s[i] == '>':
            ptr += 1
        elif s[i] == '<':
            ptr -= 1
        elif s[i] == '+':
            a[ptr] += 1
        elif s[i] == '-':
            a[ptr] -= 1
        elif s[i] == '.':
            print(chr(a[ptr]), end='')
        elif s[i] == ',':
            a[ptr] = ord(input()[0])
        elif s[i] == '[':
            if a[ptr] == 0:
                depth = 1
                i += 1
                while depth > 0:
                    if s[i] == '[':
                        depth += 1
                    elif s[i] == ']':
                        depth -= 1
                    i += 1
        elif s[i] == ']':
            if a[ptr] != 0:
                depth = 1
                i -= 1
                while depth > 0:
                    if s[i] == '[':
                        depth -= 1
                    elif s[i] == ']':
                        depth += 1
                    i -= 1
        i += 1

if __name__ == '__main__':
    S = '++++++++[>+++++++++<-]>.<+++++[>++++++<-]>-.<++[>+++<-]>+..+++.<++++++[>----<-]>.<++++++[>++++<-]>.+++.------.--------.'
    brainfuck(S)

実行するとハローワールドします。

スペース区切りの文字列をリストに格納する方法の比較

Python

競技プログラミングの入力では、一行にいくつかの数字がスペース区切りで並んでいるケースをよくみる。

例えば、No.16 累乗の加算 - yukicodera_1,a_2,...,a_N

Python3でこれらを一つのリストに格納したい時、一番早い方法はなんだろうか。

  1. map関数を使ってからlistにする
  2. リスト内包表記でmap関数
  3. ジェネレータ式からリストにする
  4. リスト内包表記でfor

それぞれに必要な時間を計測した。

import time
import random

A = [str(random.randint(0, 10000)) for i in range(5000)]
S = ' '.join(A)
times = 10000

print('method|elapsed[s]|type(A[0])')
print('---|---|---')
time_start_map = time.time()
for i in range(times):
    A = list(map(int, S.split()))
time_elapsed_map = time.time() - time_start_map
print('list(map)|', time_elapsed_map,'|',type(A[0]))

time_start_map = time.time()
for i in range(times):
    A = [map(int, S.split())]
time_elapsed_map = time.time() - time_start_map
print('[map]|', time_elapsed_map,'|',type(A[0]))


time_start_for = time.time()
for i in range(times):
    A = list(int(x) for x in S.split())
time_elapsed_for = time.time() - time_start_for
print('list(generator)|', time_elapsed_for,'|',type(A[0]))

time_start_for = time.time()
for i in range(times):
    A = [int(x) for x in S.split()]
time_elapsed_for = time.time() - time_start_for
print('[]|', time_elapsed_for,'|',type(A[0]))

結果

method elapsed[s] type(A[0])
list(map) 11.86867880821228 class 'int'
[map] 2.891165256500244 class 'map'
list(generater) 18.646066665649414 class 'int'
[] 17.813018798828125 class 'int'

結論

リスト内包表記でmap関数を使うのが爆速だけどmap形式だと意味が無いよ…… mapからlistに渡すのが早いと思っていたら4倍ほど遅かったのでびっくりしています。 びっくりはするもののこれがlist(map)が最速ですかね。