k-means法

k-means法はプロトタイプベース(prototype-based)クラスタリングというカテゴリに属している.
アルゴリズムは以下の通りである.

1.クラスタの中心の初期値として,サンプル点からk個のセントロイドをランダムに選びだす.
2.各サンプルを最も近いセントロイドに割り当てる.
3.セントロイドに割り当てられたサンプルの中心にセントロイドを移動する.
4.サンプル点へのクラスタの割当が変化しなくなるか,ユーザー定義の許容値またはイテレーションの最大回数に達するまでステップ2-3を繰り返す.

ステップ4について,オブジェクトの類似度を測定するにはm次元空間にある2つの点
xベクトルとyベクトルのユークリッド距離の2乗は
 d(x,y)^2=\sum_{j=1}^{m}(x_{j}-y_{j})^{2} = |x-y|_{2}^{2}

ABC022_C

頂点1を含まないグラフを考えてその最短経路を作れば良いと思ったが
頂点1を含まないグラフの作り方 == 頂点1に隣接している頂点の選び方O(N^2)
dijkstra法O(N^2)
よって全体でO(N^4)掛かってしまい案の定TLEだった.

#include <bits/stdc++.h>
using namespace std;
static const int WHITE = 0;
static const int GRAY = 1;
static const int BLACK = 2;
static const int INF = 1<<21;
static const int MAX_N = 300;
static const int MAX_M = 45000;
int n,m;
int res = INF;
int u[MAX_M],v[MAX_M],l[MAX_M];
int M[MAX_N][MAX_N];
int color[MAX_N],d[MAX_N];

void dijkstra(int s){//最短経路を求める
    int mincost;
    //初期化:全ての頂点uに対してcolor[u]をWHITE,d[u]をINF,d[s]=0,p[s]=-1;
    for(int u=0;u<n;u++){
        color[u] = WHITE;
        d[u] = INF;
    }
    d[s] = 0;
    color[s] = GRAY;

    while(1){
        mincost = INF;
        int u = -1;
        for(int i=0;i<n;i++){
            if(color[i]!=BLACK && d[i]<mincost){
                mincost = d[i];
                u = i;
            }
        }
        if(u==-1){
            break;
        }
        color[u] = BLACK;
        for(int v=0;v<n;v++){
            if(color[v]!=BLACK && M[u][v]!=INF){
                if(d[v]>d[u]+M[u][v]){
                    d[v] = d[u]+M[u][v];
                    color[v] = GRAY;
                }
            }
        }
    }
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<m;i++){
        cin>>u[i]>>v[i]>>l[i];//家u[i]から家v[i]までの距離がl[i]
    }
    //隣接グラフにする
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            M[i][j] = INF;
        }
    }
    for(int i=0;i<m;i++){
        M[u[i]-1][v[i]-1] = l[i];
        M[v[i]-1][u[i]-1] = l[i];
    }
    //始点1に隣接する接点を2つ選ぶ
    for(int i=0;i<n-1;i++){
        for(int j=i+1;j<n;j++){
            if(M[0][i]!=INF && M[0][j]!=INF){//始点に2つ隣接する頂点があったら
                int tmp1 = M[0][i];
                int tmp2 = M[0][j];
                M[0][i] = INF;
                M[0][j] = INF;
                dijkstra(i);
                res = min(res,tmp1 + tmp2 + d[j]);
                M[0][i] = tmp1;
                M[0][j] = tmp2;
            }
        }
    }
    if(res==INF)res = -1;
    cout<<res<<endl;
    return 0;
}

そこでwarshall_froyd法を用いた.
warshall_floyd法はグラフの全ての頂点の間の最短路を見つけるアルゴリズム.

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

typedef vector<vector<int> > Matrix;

const int INF = 1<<21;
Matrix d;//隣接行列

void warshall_floyd(int n){//n:頂点数
    for(int i=0;i<n;i++){//経由する頂点
        for(int j=0;j<n;j++){//開始頂点
            for(int k=0;k<n;k++){//終端
                d[j][k] = min(d[j][k],d[j][i] + d[i][k]);//j->kの経路がiを経由して短縮できたら更新する
            }
        }
    }
}

int main(){
    int n,m;
    cin>>n;
    d = Matrix(n,vector<int>(n,INF));//n*n行列,要素はINF
    for(int i=0;i<n;i++){
        d[i][i] = 0;
    }
    cin>>m;
    for(int i=0;i<m;i++){
        int from,to,cost;
        cin>>from>>to>>cost;
        d[from][to] = cost;
        d[to][from] = cost;
    }

    warshall_floyd(n);

    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            if(i!=j && d[i][j]!=INF){
                cout<<i<<"から"<<j<<"へのコスト:"<<d[i][j]<<endl;
            }
        }
    }
    return 0;
}

最短経路問題(ベルマンフォード法・ワーシャルフロイド法) - アルゴリズム講習会
abc022.contest.atcoder.jp
解答

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
static const int MAX_N = 300;
typedef vector<vector<int> > Matrix;

const int INF = 1<<29;
Matrix d;//隣接行列

void warshall_floyd(int n){//n:頂点数
    for(int i=1;i<n;i++){//経由する頂点
        for(int j=1;j<n;j++){//開始頂点
            for(int k=1;k<n;k++){//終端
                d[j][k] = min(d[j][k],d[j][i] + d[i][k]);//j->kの経路がiを経由して短縮できたら更新する
            }
        }
    }
}

int main(){
    int n,m;
    cin>>n;
    d = Matrix(n,vector<int>(n,INF));//n*n行列,要素はINF
    for(int i=0;i<n;i++){
        d[i][i] = 0;
    }
    cin>>m;
    for(int i=0;i<m;i++){
        int from,to,cost;
        cin>>from>>to>>cost;
        d[from-1][to-1] = cost;
        d[to-1][from-1] = cost;
    }

    //WFで頂点1以外の最短経路を更新
    warshall_floyd(n);

    //頂点1から隣接した2点に
    int res = INF;
    for(int i=1;i<n-1;i++){
        for(int j=i+1;j<n;j++){
            res = min(res,d[0][i]+d[0][j]+d[i][j]);
        }
    }
    cout<<(res!=INF?res:-1)<<endl;
    return 0;
}

ABC007_C

幅優先探索の問題
queueにいれて順番に計算していく.
座標を扱う際にtypedefが便利

typedef pair<int,int> P;
que.push(P(x,y));
#include <bits/stdc++.h>
using namespace std;
int r,c,sx,sy,gx,gy;
int dx[4] = {1,0,-1,0},dy[4] = {0,1,0,-1};
static const int MAX_R =50;
static const int INF = 1<<21;
char M[MAX_R][MAX_R+1];
int D[MAX_R][MAX_R];
typedef pair<int,int> P;

int main()
{
    queue<P> que;
    cin>>r>>c>>sy>>sx>>gy>>gx;
    sy--;sx--;gy--;gx--;
    string tmp;
    for(int y=0;y<r;y++){
        cin>>tmp;
        for(int x=0;x<c;x++){
            M[y][x] = tmp[x];
            D[y][x] = INF;
        }
    }
    D[sy][sx] = 0;
    que.push(P(sx,sy));
    while(!que.empty()){
        P p = que.front();que.pop();
        int x = p.first;
        int y = p.second;
        if(x==gx && y==gy)break;
        for(int i=0;i<4;i++){
            int nx = x + dx[i];
            int ny = y + dy[i];
            if(0<=nx && nx<c && 0<=ny && ny<r && M[ny][nx]=='.' && D[ny][nx]==INF){
                que.push(P(nx,ny));
                D[ny][nx] = D[y][x] + 1;
            }    
        }
    }
    cout<<D[gy][gx]<<endl;
    return 0;
}

【c++】vectorの重複要素の削除

unique()は配列の長さは変わらないため重複で削除された空きの場所にゴミが入る

#include <bits/stdc++.h>
using namespace std;
void printVector(vector<int> &v){
    for(int i=0;i<v.size();i++){
        cout<<v[i]<<endl;
    }
}
int main()
{
    vector<int> vec;
    vec.push_back(1);
    vec.push_back(2);
    vec.push_back(2);
    vec.push_back(3);
    vec.push_back(3);
    vec.push_back(3);
    printVector(vec);
    unique(vec.begin(),vec.end());
    cout<<"-----------------------"<<endl;
    printVector(vec);
    return 0;
}
/*
1
2
2
3
3
3
-----------------------
1
2
3
3//ゴミ
3//ゴミ
3//ゴミ
*/

あらかじめソートして,vec.erase()で重複部分を消す.
unique(vec.begin(),vec.end())はゴミの前を指している.
そこからvec.end()までのゴミ部分を削除するとうまく重複部分を消すことができる.

#include <bits/stdc++.h>
using namespace std;
void printVector(vector<int> &v){
    for(int i=0;i<v.size();i++){
        cout<<v[i]<<endl;
    }
}
int main()
{
    vector<int> vec;
    vec.push_back(1);
    vec.push_back(2);
    vec.push_back(2);
    vec.push_back(3);
    vec.push_back(3);
    vec.push_back(3);
    printVector(vec);
    sort(vec.begin(),vec.end());//ソート
    vec.erase(unique(vec.begin(),vec.end()),vec.end());
    cout<<"-----------------------"<<endl;
    printVector(vec);
    return 0;
}
/*
1
2
2
3
3
3
-----------------------
1
2
3
*/

重複していない部分のサイズを取得するには

int* a = unique(vec.begin(),vec.end());//ゴミの手前を指す
int* b = vec.begin();//配列の先頭を指す
cout<<a-b<<endl;//重複していないサイズ

ABC019_C

2で割り切れたら割り続けて集合に入れる.
2で割り切るのは右ビットシフトと同義

a[i] /= 2;
a[i] = a[i]>>1;

a[i] =>>1;//これはダメ
a[i]>>1;//これもダメ

解答

#include <bits/stdc++.h>
using namespace std;
#define ll long long int
static const int MAX_N = 100000;
set<int> S;
int a[MAX_N];
int main(){
    int n;cin>>n;
    for(int i=0;i<n;i++)cin>>a[i];
    for(int i=0;i<n;i++){
        while(a[i]>1 && !(a[i]%2)){//2で割り続ける
            a[i]=a[i]>>1;
        }
        S.insert(a[i]);
    }
    cout<<(int)S.size()<<endl;
    return 0;
}

ABC030_C

飛行機で何往復できるかという問題
普通にやると大変なのでlower_bound()による二分探索を使った.
lower_bound(a,a+n,value)はvalue以上の値の位置を返す.

#include <bits/stdc++.h>
using namespace std;
int main(){
    int a[5] = {1,3,5,7,9};
    int* pos = lower_bound(a,a+5,8);//8を初めて越すのはa[4]:9
    cout<<pos-a<<endl;//4
    cout<<*pos<<endl;//9
    return 0;
}

配列aの範囲内に有るか無いかを条件文で判断するには

if(pos-a<n)//ある
else //ない

を用いた.
もっと良い方法がありそうだが…
解答

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int n,m,x,y;
    int a[100000],b[100000];
    cin>>n>>m>>x>>y;
    for(int i=0;i<n;i++)cin>>a[i];
    for(int i=0;i<m;i++)cin>>b[i];
    int cur = 0;
    int *pos;
    int res = 0;
    int flag = 1;//flag = 1ならばAにいる
    while(1){
        if(flag){
            pos = lower_bound(a,a+n,cur);//curより大きい最初の位置
            if(pos-a<n){
                cur = *pos+x;//飛行機に乗る
                flag = 0;
            }else{
                break;
            }
        }else{
            pos = lower_bound(b,b+m,cur);
            if(pos-b<m){
                cur = *pos+y;
                res++;
                flag = 1;
            }else{
                break;
            }
        }
    }
    cout<<res<<endl;
    return 0;
}

アニメop顔検出器+分類器

今後すること

  • トレーニング画像の種類を増やす.
  • 出力層の出力を増やして分類の数を増やす.

【Python】namedtupple

>>>from collections import *
>>> # Basic example
>>> Point = namedtuple('Point', ['x', 'y'])
>>> p = Point(11, y=22)     # instantiate with positional or keyword arguments
>>> p[0] + p[1]             # indexable like the plain tuple (11, 22)
33
>>> x, y = p                # unpack like a regular tuple
>>> x, y
(11, 22)
>>> p.x + p.y               # fields also accessible by name
33
>>> p                       # readable __repr__ with a name=value style
Point(x=11, y=22)

8.3. collections — コンテナデータ型 — Python 3.6.1 ドキュメント


tensorflowのdata_input()でMNISTを読み込んだ時のMNISTのデータ型

print(type(mnist))
<class 'tensorflow.contrib.learn.python.learn.datasets.base.Datasets'>

TensorFlow内での定義

Datasets = collections.namedtuple('Datasets', ['train', 'validation', 'test'])#line 37

github.com

CNNで画像の前処理

判定処理を行う入力画像の前処理
1.画像の周囲を切り落とす(Cropping)
2.画像のダイナミックレンジを平準化する(Whitening)

トレーニングデータとして入力する画像にする前処理
1.画像の周囲をランダムに切り落とす(Random Cropping)
2.画像をランダムに左右反転する(Random Flipping)
3.画像の明るさとコントラストをランダムに変更する
4.画像のダイナミックレンジを平準化する(Whitening)