LoginSignup
1
1

More than 5 years have passed since last update.

十文字に反転して色を揃えて!(std::async)

Last updated at Posted at 2016-10-03
シリーズ:掃き出し法について URL
問題:十文字に反転して色を揃えて!(最短手数の最大化) https://codeiq.jp/q/2972
single/OpenMP https://qiita.com/cielavenir/items/2fa1f1149132de579a46
std::async https://qiita.com/cielavenir/items/61b329936ef6508a5938
番外編:オンラインジャッジで並列実行は使えるか? https://qiita.com/cielavenir/items/ade2b7c0b5a63b2687f0
問題:裏表ちわーわ(解の存在判定) http://yukicoder.me/problems/no/460
https://www.codeeval.com/public_sc/191/
解説 https://qiita.com/cielavenir/items/c6b15ff4e28fb5f492ec

(概要等はsingle/OpenMP版を参照下さい)

  • 今まで並列プログラミングにはOpenMPを使ってきたが、std::asyncをvectorに突っ込むことでもできるらしいので、やってみた。
tyama_codeiq2972_async.cpp
// async
// 5 5: 0.08s
// 3 9: 1.47s
// 5 6: 15.53s
// 4 8: 83.18s
// 6 6: 772.15s

#include <vector>
#include <algorithm>
#include <future>
#include <cstdio>
using namespace std;

typedef unsigned long long val_t;
#define popcnt __builtin_popcountll
//typedef unsigned int val_t;
//#define popcnt __builtin_popcount
//typedef mpz_class val_t;
//int popcnt(const val_t &x){int r=0;val_t z=x;for(;z;z/=2)r+=z%2;return r;}

int lightsout(int x,int y){
    vector<vector<val_t>>a(x*y);
    for(int i=0;i<x*y;i++)a[i].resize(2);

    //create problem
    for(int i=0;i<x;i++){
        for(int j=0;j<y;j++){
            a[i+j*x][0]=(val_t)1<<(i+j*x);
            a[i+j*x][1]= 0 +
                ((val_t)1<<(i+j*x)) +
                (i>0   ? (val_t)1<<(i-1+j*x) : 0) +
                (i<x-1 ? (val_t)1<<(i+1+j*x) : 0) +
                (j>0   ? (val_t)1<<(i+(j-1)*x) : 0) +
                (j<y-1 ? (val_t)1<<(i+(j+1)*x) : 0) +
                0;
        }
    }

    //solve
    val_t badbits=0;
    int i=0;
    for(;i<x*y;i++){
        if((a[i][1]&((val_t)1<<i))==0){
            int j=i+1;
            for(;j<x*y;j++){
                if((a[j][1]&((val_t)1<<i))!=0){
                    swap(a[i],a[j]);
                    break;
                }
            }
            if(j==x*y){
                badbits|=(val_t)1<<i;
                continue;
            }
        }

        for(int j=0;j<x*y;j++){
            if(i==j)continue;
            if((a[j][1]&((val_t)1<<i))!=0){
                a[j][0]^=a[i][0];
                a[j][1]^=a[i][1];
            }
        }
    }
    int k=x*y-popcnt(badbits);
    fprintf(stderr,"quiet pattern=%d\n",x*y-k);

    //0解(quiet pattern)の集合tを用意する
    val_t tmsk=0;
    vector<val_t>t;
    vector<pair<int,val_t>>a_ok;
    for(int i=0;i<x*y;i++){
        if((badbits>>i)&1){
            t.push_back(a[i][0]);
        }else{
            a_ok.emplace_back(i,a[i][0]);
            tmsk|=(val_t)1<<i;
        }
    }

#if 0
    //入力・解の存在判定
    val_t input=0;
    for(int i=0;i<x*y;i++){
        int t;
        scanf("%d",&t);
        input|=(val_t)t<<i;
    }
    if(any_of(t.begin(),t.end(),[&](val_t &e)->bool{
        return popcnt(e&input)&1;
    })){
        return -1;
    }
#endif

    vector<val_t>tlst(1<<(x*y-k)); // このメモリはあまり大きくならないはず
    for(val_t l=0;l<1<<(x*y-k);l++){
        val_t r=0;
        for(int j=0;j<x*y-k;j++)if(l&((val_t)1<<j))r^=t[j];
        tlst[l]=r;
    }

    //全盤面に対し最短手数を計算する
    int num_threads=thread::hardware_concurrency();
    auto f=[&](val_t start)->int{
        int r=0;
        for(val_t i_=start;i_<(val_t)1<<k;i_+=num_threads){
            //i_のiterationは本来はbadbitsを除いたビット
            //ライツアウトではbadbitsは末端に集中しているためこれで良い
            //本来はtlist/a_okのビットをbadbits以外に詰めるのが正解

            if(popcnt(i_)*2>k)continue;
            //完全反転についても求め、最小値を取る
            int c=1<<29;
            for(val_t i:{i_,i_^tmsk}){
                val_t r0=0;
                for(auto &j:a_ok)if(i&((val_t)1<<j.first))r0^=j.second;
                //0解の重ね合わせをすべて試す
                int c0=1<<29;
                for(val_t l=0;l<(val_t)1<<(x*y-k);l++){
                    val_t r1=r0;
                    //for(int j=0;j<x*y-k;j++)if(l&((val_t)1<<j))r1^=t[j];
                    r1^=tlst[l];
                    c0=min(c0,popcnt(r1));
                }
                c=min(c,c0);
            }
            r=max(r,c);
        }
        return r;
    };
    vector<future<int>>task;
    for(int i=1;i<num_threads;i++)task.push_back(async(launch::async,f,i));
    int r=f(0);
    for(auto &t:task)r=max(r,t.get());
    return r;
}

int main(){
    int m,n;
    scanf("%d%d",&m,&n);
    printf("%d\n",lightsout(m,n));
}
  • やはり効率はOpenMPに比べると落ちるらしい。
  • ただ、C++11標準なので、clangでも使えるのが良いですね。
1
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1