| × [PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。 | 
|  | 
| atcoder141Eを解いていたら良い方法が思い浮かばず、解説読んでKMPアルゴリズムを知った。  E - Who Says a Pun? KMP法(by wikipedia) これの面白いところは、BM法と違いは何文字一致したかまで確認出きるという点かな。 勉強になったなり。 ちなみに、解説にはZアルゴリズムとあったのだけども、すべての部分文字列について知りたいわけではないので、KMPのテーブル生成のみに着目したほうがパフォーマンスは良さげ。 しゃくとり法を使うともっとシンプルに解けていることが衝撃である。 PR | 
|  | 
| javaでflatMapを覚えてから奇妙だなと思っていた。 これって何に使うのだろうかと。 結論から言うと直積を求めたいときに使うのである。 もし、組み合わせを求めたい場合、どのように求めるだろうか。 ■pythonソース teams = range(5) [(x, y) for xi, x in enumerate(teams) for yi, y in enumerate(teams) if xi < yi]■python結果 [(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)] 実はこれと同じことをflatMapでも行える。 ■javascriptソース range = (x)=>function*(){ for(var i = 0;i<x;i++) {yield i;}};
teams = range(5);
Array.from([[]])
.flatMap(x=>Array.from(teams()).map(y=>x.concat(y)))
.flatMap((x, xi)=>Array.from(teams()).filter((y,yi)=>xi<yi).map(y=>x.concat(y)))
■javascript結果(10) [Array(2), Array(2), Array(2), Array(2), Array(2), Array(2), Array(2), Array(2), Array(2), Array(2)] 0: (2) [0, 1] 1: (2) [0, 2] 2: (2) [0, 3] 3: (2) [0, 4] 4: (2) [1, 2] 5: (2) [1, 3] 6: (2) [1, 4] 7: (2) [2, 3] 8: (2) [2, 4] 9: (2) [3, 4] length: 10 __proto__: Array(0)おもしろいなあ。 | 
|  | 
| グラフを作りたかったので、graphvixを使ってみました。 【参考】Boost.Graph Graphviz形式で重みを出力 #include <fstream>
#include <vector>
#include <string>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
struct EdgeWithInfo
{
    int nodeS;
    int nodeE;
    int weight;
    std::string name;
};
typedef boost::adjacency_list<boost::listS, boost::vecS, boost::directedS,
                              boost::no_property, boost::property<boost::edge_all_t, EdgeWithInfo>>
    Graph;
typedef std::pair<int, int> Edge;
enum
{
    A,
    B,
    C,
    D,
    E,
    N
};
const std::string name[] = {"A", "B", "C", "D", "E"};
int main()
{
    const std::vector<EdgeWithInfo> edgeWithInfo =
        {
            {A, B, 3, "→B"},
            {A, C, 1, "→C"},
            {A, D, 4, "→D"},
            {B, E, 2, "→E"},
            {C, E, 5, "→E"},
            {D, E, 6, "→E"}};
    std::vector<Edge> edges;
    std::transform(edgeWithInfo.begin(), edgeWithInfo.end(), std::back_inserter(edges),
                   [](auto ei) {
                       return std::make_pair(ei.nodeS, ei.nodeE);
                   });
    const Graph g(edges.begin(), edges.end(), edgeWithInfo.begin(), N);
    std::ofstream file("test.dot");
    boost::write_graphviz(file,
                          g,
                          boost::make_label_writer(name),
                          [](auto &graph_) {
                              return [&graph_](std::ostream &out, auto edge) {
                                  const EdgeWithInfo &ewi =
                                      *reinterpret_cast<const EdgeWithInfo *>(&boost::get(boost::edge_all, graph_, edge));
                                  out << "[";
                                  out << "weight=" << ewi.weight << ",";
                                  out << "label=" << ewi.name;
                                  out << "]";
                              };
                          }(g));
}
digraph G {
0[label=A];
1[label=B];
2[label=C];
3[label=D];
4[label=E];
0->1 [weight=3,label=→B];
0->2 [weight=1,label=→C];
0->3 [weight=4,label=→D];
1->4 [weight=2,label=→E];
2->4 [weight=5,label=→E];
3->4 [weight=6,label=→E];
}
正攻法ではない気がするけど、まあ良しとしよう。 | 
|  | 
| Pythonでの不満。Python2にあったsorted関数のcmpがなくなって、keyになった。 なぜ、cmpはなくなったのだろうか?考えてみた。 まず、sortedへの不満として、複数キーの昇順・降順ができなくなった。というものがあった。 誤りである。 どうやらkeyにタプルを返すと勝手に複数キーの昇順としてソートしてくれるようだ。 つまり、文字は反転。数字はマイナスにすることで、それらしきものができる。 c=[('a', 1), ('a', 2), ('b', 2), ('b', 1)]
sorted(c, key=lambda x: (tuple(map(lambda z:(0xff-z), x[0].encode("cp932"))), -x[1]))
[('b', 2), ('b', 1), ('a', 2), ('a', 1)]
ここで気づいた。この比較用キーの作成はcmpの場合、ソート方法に依存してしまう。O(2*NlogN)の頻度で比較用キーが作成されてしまうのだ。この点、key指定だと比較用キーはO(N)のみの作成となる。 すばらしい ただし、文字列の降順としては可変長の場合、前述の方法は使えない。なので、classを作ってみた。 class Keys:
    def __init__(self, v, isAscs):
        self.v = v
        self.isAscs = tuple(isAscs)
    def __lt__(self, other):
        for i, s, isAsc, o in zip(range(len(self.isAscs)-1, -1, -1), self.v, self.isAscs, other.v):
            lt = [lambda p1, p2: p1 > p2, lambda p1, p2: p1 < p2][isAsc]
            ret = lt(s, o)
            if ret or i and lt(o, s):
                break
        return ret
    @staticmethod
    def of(isAscs, keymap=lambda x:x):
        return lambda item: Keys(keymap(item), isAscs)
if __name__ == "__main__":
    arr = [("a", 2), ("b", 1), ("a", 1), ("b", 2)]
    print(arr)
    #1項目め降順、2項目め昇順
    print(sorted(arr, key=Keys.of((False, True))))
    #1項目め2文字目昇順、1項目め1文字目昇順
    print(sorted(arr, key=Keys.of((True, False), keymap=lambda x:(x[0][1], x[0][0]))))
うん、素晴らしい。 | 
|  | 
| C++17でモンテカルロを並列化してみた。に触発されて、GNU版で作ってみました ■コンパイル $ g++ test.cc -Wall -march=native -std=c++17 -O3 -fopenmp■ソース #include <parallel/algorithm>
//#include <execution>
#include <atomic>
#include <mutex>
#include <iostream>
#include <random>
#include <array>
#include <stdlib.h>
int main(int argc, char *argv[])
{
    static int const NUM = 1000000000;
    static int threads = 8;
    static_assert(std::atomic<int>::is_always_lock_free);
    if (argc >= 2)
    {
        threads = atoi(argv[1]);
    }
    auto nums = std::vector<int>(threads);
    for (auto &num : nums)
    {
        num = NUM / threads;
    }
    __gnu_parallel::_Settings s;
    s.algorithm_strategy = __gnu_parallel::force_parallel;
    __gnu_parallel::_Settings::set(s);
    std::atomic counter = {0};
    __gnu_parallel::for_each(nums.begin(), nums.end(), [&counter](int num) {
        std::random_device rnd;
        thread_local std::mt19937 mt(rand());
         std::uniform_real_distribution<double> score(0.0, 1.0);
        for (auto &&no = 0; no < num; ++no)
        {
            auto &&x = score(mt);
            auto &&y = score(mt);
            if ((x * x + y * y) < 1.0)
            {
                counter++;
            }
        }
    });
    std::cout << "PI = " << 4.0 * counter / NUM << std::endl;
}
■結果$ time ./a.out 1 PI = 3.14155 real 0m27.461s user 0m27.469s sys 0m0.001s $ time ./a.out 2 PI = 3.14159 real 0m29.238s user 0m58.008s sys 0m0.016s $ time ./a.out 3 PI = 3.14151 real 0m28.447s user 1m24.458s sys 0m0.000s $ time ./a.out 4 PI = 3.14155 real 0m32.101s user 2m5.238s sys 0m0.020s なぜか延べ時間(user)が増えるだけでreal時間が減りません。。。 そこでソースを修正してみました。 ■ソース修正後 #include <parallel/algorithm>
//#include <execution>
#include <atomic>
#include <mutex>
#include <iostream>
#include <random>
#include <array>
#include <stdlib.h>
int main(int argc, char *argv[])
{
    static int const NUM = 1000000000;
    static int threads = 8;
    static_assert(std::atomic<int>::is_always_lock_free);
    if (argc >= 2)
    {
        threads = atoi(argv[1]);
    }
    auto nums = std::vector<int>(threads);
    for (auto &num : nums)
    {
        num = NUM / threads;
    }
    __gnu_parallel::_Settings s;
    s.algorithm_strategy = __gnu_parallel::force_parallel;
    __gnu_parallel::_Settings::set(s);
    std::atomic counter = {0};
    __gnu_parallel::for_each(nums.begin(), nums.end(), [&counter](int num) {
        std::random_device rnd;
        thread_local std::mt19937 mt(rand());
        int _counter = 0;
        std::uniform_real_distribution<double> score(0.0, 1.0);
        for (auto &&no = 0; no < num; ++no)
        {
            auto &&x = score(mt);
            auto &&y = score(mt);
            if ((x * x + y * y) < 1.0)
            {
                _counter++;
            }
        }
        counter += _counter;
    });
    std::cout << "PI = " << 4.0 * counter / NUM << std::endl;
}
■結果$ time ./a.out 1 PI = 3.14155 real 0m20.263s user 0m20.255s sys 0m0.000s $ time ./a.out 2 PI = 3.14159 real 0m10.117s user 0m20.223s sys 0m0.000s $ time ./a.out 3 PI = 3.14151 real 0m7.409s user 0m22.209s sys 0m0.000s $ time ./a.out 4 PI = 3.14155 real 0m6.129s user 0m24.492s sys 0m0.001score4個分で20秒から6秒にその後、8個に増やしても増えませんでした。。。 なかなかのパフォーマンスなり。 | 
|  | 
| 忍者ブログ [PR] | 
 


