【C++】std::setの基本的な使い方

setはユニークな要素を格納する連想コンテナの1つです.連想コンテナとはキーと値のペアを保持するクラスを指し,setの場合はインデックスがキーとなります. setは二分木として実装されているため,キーから値をとりだすのがO(logN)と高速です. ただしvectorとは異なり要素がユニークである必要があるので,使用の際は用途に合っているか確認する必要があります. (例えばマイナンバーのように値が一意であることが保証されていればsetが適している.一方で名簿のように同姓同名の人が含まれるケースも考える場合にsetは使うべきでない)

setの使い方

基本的な使い方

  • 初期化:set<ValueType> 変数名;
  • 要素の追加:insert()関数を用いる
  • 要素の削除:erase()関数を用いる
  • 値の参照:イテレータや範囲for文を用いる
    二分木で実装されているため,vectorなどで用いられる[]演算子でのアクセスはできない.
  • 値の検索:
    • 値の一致する要素のiteratorを得るにはfind()関数を用いる
    • 値の一致する要素があるかのみを確認した場合はcount()関数を用いる(返り値は0 or 1)
#include <iostream>
#include <set>

using namespace std;

int main() {
  // 変数の宣言
  set<int> s;

  // 値を挿入
  s.insert(1);
  s.insert(2);
  s.insert(3);
  s.insert(4);
  s.insert(5);

  // 値を削除
  auto c = s.erase(5);  // 5は存在するのでeraseの結果1が返される
  c = s.erase(4);  // 4は存在するのでeraseの結果1が返される
  c = s.erase(5);  // 5は既に削除済みなので,eraseの結果0が返される

  // 値の参照
  // iteratorを使う場合
  auto iter = s.begin();  // iterの方はstd::set<ValueType>::iteratorであるが,通常autoで受ける
  cout << *iter << endl;  // 1
  cout << *(iter + 1) << endl;  // 2

  // 範囲for文を使う場合
  for (auto x : s) {
    cout << *x << endl;
  }

  // 値の検索
  auto find_iter = s.find(3);  // O(logN)で3を探索
  if (find_iter != s.end()) {
    cout << "Found" << endl;
  } else {
    cout << "Not found" << endl;
  }

  if (s.count(3)) {
    cout << "Found" << endl;
  } else {
    cout << "Not found" << endl;
  }
}

その他

  • bool empty():setの要素が空かを判定
  • size=t size():setのユニークな要素の数を返す
  • void clear():setの全要素を削除する
  • イテレータを使ってvectorをsetに変換することができる
  size_t size = s.size();
  cout << "size: " << size << endl;  // size: 3
   
  s.clear();
  bool isEmpty = s.empty();  // true

  vector<int> v{1, 2, 3, 4, 5, 3};
  set<int> converted_s(v.begin(), v.end());
  

【C++】std::pair, tupleの基本的な使い方

2つの異なる型の値を「組」として保持するpairクラスについて使い方をまとめます. 類似のもので,より一般にN個の異なる型の値を扱うtupleクラスがあるのでこちらについても簡単に紹介します.

pairの使い方

初期化と要素へのアクセス

  • make_pair()というヘルパー関数を用いて初期化
    2つの値の組を設定する.
  • object.first, object.secondで要素へアクセス
#include <iostream>
#include <string>
#include <utility>

using namespace std;

int main() {
  std::pair<int, string> p = std::make_pair(1, "first");  // 
  cout << p.first << endl;  // 1
  cout << p.second << endl;  // "first"

  return 0;
}

vectorと合わせた利用法

例として,名前と年齢の組を複数持つ名簿のようなものを作るケースを考えます. ここではこの名簿をvectorとpairを組み合わせることで実現します(重複がないのであれば,vectorよりもsetを使う方が適切かもしれません). 下記の例では途中でソートする処理を入れています.この場合,ソートはpairの1つ目,2つ目の要素に対して順に作用します.

#include <iostream>
#include <string>
#include <utility>
#include <vector>

using namespace std;

int main() {
  vector<pair<string, int>> v;
  v.push_back(make_pair("Tom", 15));
  v.push_back(make_pair("Ken", 13));
  v.push_back(make_pair("Alice", 14));
  v.push_back(make_pair("Alice", 16));

  sort(v.begin(), v.end());
  for (auto iter = v.begin(), iter != v.end(); ++iter) {
    cout << "Name: " << *iter.first << " Age: " << *iter.second << endl;
  }

  /*
  Name: Alice Age: 14
  Name: Alice Age: 16
  Name: Ken Age: 13
  Name: Tom Age: 15
  */

  return 0;
}

tupleの使い方

初期化と要素へのアクセス

  • make_tuple()というヘルパー関数を用いて初期化
    pairに似ているが,pairは2つの値の組であったのに対しtupleは任意のN個の値の組を扱うことができる.

  • std::get<i>(object)でi番目の要素へアクセス

以下の例ではtupleにx, y, z座標を格納し,それぞれの座標を表示させる例である.

#include <iostream>
#include <string>
#include <tuple>

using namespace std;

int main() {
  // (x, y, z)座標をtupleで定義
  std::tuple<float, float, float> t = std::make_tuple(1.0, 0.0, -2.0);
  cout << "x: " << get<0>(p) << endl;  // x: 1.0
  cout << "y: " << get<1>(p) << endl;  // y: 0.0
  cout << "z: " << get<2>(p) << endl;  // z: -2.0

  return 0;
}

【C++】std::vectorの基本的な使い方

動的配列vectorについて使い方のメモ. 適宜追記していく.

初期化

1次元

#include <vector>

using namespace std;

void initialize() {
  // 空のvectorとして初期化
  vector<int> vec1;

  // 要素数(10)を指定して初期化
  vector<int> vec2(10);

  // 要素数(10)と初期値(0)を指定して初期化
  vector<int> vec3(10, 0);

  // データ列を指定して初期化
  vector<int> vec4{1, 2, 3, 4};  
}

多次元

#include <vector>

using namespace std;

void initialize() {
  // 
  vector<vector<int>> m_vec1(10, vector<int>(5, 0));

  // 
  vector<vector<int>> m_vec2 = {
    {0, 1, 2, 3},
    {4, 5, 6, 7},
    {8, 9, 10, 11},
  };
}

要素の参照

#include <vector>

using namespace std;

void ref() {
  vector<int> vec(10);
  for (int i = 0; i < 10; i++) {
    vec[i] = 1;  // vec.at(i)でもOK
  }

  vector<vector<int>> m_vec(10, vector<int>(5, 0));
  for (int i = 0; i < 10; i++) {
    for (int j =0; j < 5; j++) {
      m_vec.at(i).at(j) = i * j;  // 多次元ベクトルへのアクセスはat(index)を複数回使う
    }
  }
}

イテレータ

  • イテレータは抽象化されたポインタ
  • *演算子で要素の参照・変更ができる
  • イテレータのインクリメント・デクリメントで前後の要素へ移動できる
vector<int> v{1, 2, 3, 4};
auto iter = v.begin();  // vector<int>::iterator iter = v.begin(); でもOKだが,記述が面倒なので型推論のautoを通常使う
cout << *iter << endl;  // 1と表示
iter++;
cout << *iter << endl;  // 2と表示
*iter = 9;  // 2番目の要素を9に変更
cout << *iter << endl;  // 9と表示

イテレータを使ったLoop

vector<int> v{1, 2, 3, 4};

for (auto iter = v.begin(); iter != v.end(); ++iter) {
  cout << *iter << endl;
}

アルゴリズム

count:値が一致する要素のカウント

vector<int> v{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};

int n = count(v.begin(), v.end(), 5);  // 5に一致する要素数を返す

int i = 2, j = 7;
int n_ranged = count(v.begin() + i, v.begin() + j, 5);  // 3(= 2+1) ~ 8(= 7+1)の範囲で5に一致する要素数を返す

sort:要素のソート

vector<int> v{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
sort(v.begin(), v.end());  // {1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9}のように昇順にソートされる

reverse:順序の反転

vector<int> v{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
reverse(v.begin(), v.end());  // {5, 3, 5, 6, 2, 9, 5, 1, 4, 1, 3}のように逆順にソートされる

// 降順にソート = 昇順ソート + 反転
vector<int> v{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
sort(v.begin(), v.end());
reverse(v.begin(), v.end());

find:値が動的配列に含まれるかのチェック

  • find(v.begin(), v.end(), value)はvを最初から最後まで走査し,valueに一致する初めての要素のイテレータを返す
  • どの要素とも一致しなかった場合は,v.end()を返す
vector<int> v{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
auto iter = find(v.begin(), v.end(), 5);

if (iter != v.end()) {
  // 見つかった場合の処理
}

素数が多い場合

  • 要素が変わらず,かつ要素が含まれるかの探索を何度も行う場合は2分探索の方が効率が良い
    • sortはO(N*logN)なので毎回sortが必要になるとかえって遅くなる
    • sortが1度で済む(=要素が不変な場合)は効率が良い
  • findはO(N),2分探索はO(logN)

max(min)_element:最大値・最小値とその要素を見つける

#include <vector>
#include <numeric>

int main() {
  vector<int> v{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
  // *_element は,イテレーターを返すので '*' で値を取得する
  vector<int>::iterator min_iter = min_element(v.begin(), v.end());
  vector<int>::iterator max_iter = max_element(v.begin(), v.end());
  cout << "min: " << *min << ", " << "max: " << *max << endl;

  // distance で vec の先頭イテレーターと minIt, maxIt との距離を取得する.
  // インデックスを取得したいときは,vec の先頭イテレーターを指定する必要がある.
  // 例えば,vec.begin() + 1 とか指定すると答えは変わる.
  size_t minIndex = std::distance(vec.begin(), min_iter);
  size_t maxIndex = std::distance(vec.begin(), max_iter);

  return 0;
}


### accumulate:要素の積算

include

include

int accumulate() { vector v{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5}; int sum = accumulate(v.begin(), v.end()); return sum; }

##  参考
- [http://vivi.dyndns.org/tech/cpp/vector.html:title]
- [https://cpprefjp.github.io/reference/vector/vector.html:title]
- [https://atcoder.jp/contests/APG4b/tasks/APG4b_t?lang=ja:title]

AWSのEC2インスタンスにRemote Desktopでつなぐ

AWSのEC2インスタンスにRemote Desktopでつなぐ

個人メモです. DockerでROSを起動するのは困難であったため,AWSでROS環境を構築する方針に切り替えます.

osazo-ml-cv.hatenablog.com

以下の記事を参考にすることでAWSのEC2 Ubuntu18.04インスタンスリモートデスクトップを介してつなぐことができました.
AWS EC2でデスクトップ環境をつくる ~ Ubuntu Server 18.04 LTS GNOME編~

f:id:osazo:20200406065918p:plain

GUI上でのkeyboard設定も行ったのですが何故か反映されず.
ここはCloud9を使えばコーディングについては問題なくできるかな?
今度はAWSが提供するロボット開発環境であるRoboMakerで環境作成を行ってみます.

macOSにDockerをインストールし,ubuntuでROSを動かす

macOSにDockerをインストールし,ubuntuでROSを動かす

タイトルの通りです.手順をメモします.

Dockerのインストール

  • Docker Desktop for Macをダウンロード.
  • インストーラーを走らせる.
  • Dockerのアカウントを作成する.
  • LaunchpadからDockerをクリックし,Docker Desktopを起動する.
  • 正常にインストール,起動していることをターミナルで確認.

      $ docker info
    

ROSイメージを起動

  • イメージをpullする.
    perceptionに関するタスクを扱いたいので,下記のtagのものを選択.

      $ docker pull ros:melodic-perception-bionic
    
  • コンテナを起動する. ホストと共有のディレクトリを作っておくと作業が楽になります.

      $ docker run -it --name docker_ros --net='host' -v ~/Desktop/sandbox/:/sandbox/ ros:melodic-perception-bionic /bin/bash
    

中身を見てみると,opencv,pclなどPerceptionに必要な基本ライブラリがすでにインストールされいました.

GUIが開かない件を解決する

rvizを起動しようとしたところ,Could not connect to any X display.と怒られます.
どうやらx serverの設定がうまく出来ていないようです.
こちらのLinkに従って対応します.

  • XQuartzをインストール
  • XQuartzを起動し,環境設定 --> セキュリティタブの"ネットワーク・クライアントからの接続を許可"にチェックを入れる.
  • Indirect GLXを有効にする.

      $ defaults write org.macosforge.xquartz.X11 enable_iglx -bool true
    
  • XQuartzを再起動

  • x serverにlocalhostを登録し,DISPLAY環境変数を指定してコンテナを起動.

      $ xhost + 127.0.0.1
      $ docker run -it --name docker_ros --net='host' -v ~/Desktop/sandbox/:/sandbox/ -e DISPLAY=docker.for.mac.localhost:0  -v="/tmp/.X11-unix:/tmp/.X11-unix:rw" --privileged ros:melodic-perception-bionic /bin/bash
    

rvizを起動するもsegmentation faultで落ちる.ムムム…
結局この問題を解決できずにmacでROSをイジるのは諦めた. そこそこのスペックでいいからubuntuマシンを自宅に欲しいところ.
特にリモートワークが当たり前になるとねぇ.

Semantic SegmentationのためのActive Learning

Semantic SegmentationのためのActive Learning

最近興味を持っているテーマです. Googlingして目について論文をメモします.完全に個人用です.

Active Learning for Semantic Segmentation

他にも論文はちょこちょこあるのだけれども,Deep Learningがメジャーになる前のものが多い印象.
全体的にはBlue Oceanかも?

VSCodeを使ったC++の環境構築【macOS】

VSCodeを使ったC++の環境構築【macOS

更新日:2022/12/21 M1 Macの場合は下記を参照

【競プロ】AtCoderを始めるためのC++の基本 | そまちょブログ

Visual studio codeで競プロ環境構築[mac OS] - Qiita


更新日:2020/03/13 VSCodeの更新が盛んなので,時間がたつと記事がアテにならなくなるとのこと.


1. 下準備

2. コンパイルから実行まで

2.1. サンプルコードの作成

$ mkdir sample && cd sample
$ touch main.cpp
#include <iostream>

using namespace std;

int main(){
    int year = 1986, month = 5, date = 5;

    cout << "hello world: " << endl
         << "birthday is " << year << month << date << endl;
    return 0;
}

2.2. コンパイラPathの設定

gccをinstallすると,Mac上に2つのコンパイラ(clang, gcc)が同居する状態になる.それぞれのPathは,

  • (clang) /usr/bin/g++
  • (gcc) /usr/local/bin/g++-9

競技プラグラミングでは<stdc++.h>というライブラリを使うのが便利で,それはgccにのみ入っているということ. そこで,ここではgccコンパイラを使うように設定を行っていきます.

まずはg++コマンドで/usr/local/bin/g++-9が呼ばれるようにシンボリックリンクを貼ります.

$ ln -s /usr/local/bin/g++-9 /usr/local/bin/g++
$ which g++  >>  /usr/local/bin/g++(/usr/bin/g++から変わりました)

ちなみに優先順位が,/usr/local/bin/g++ > /usr/bin/g++となるのは環境変数$PATHの設定によるものです.

$ echo $PATH
/usr/local/bin:/usr/local/sbin:/usr/bin

2.3. Code Runnerの設定

VSCode上からC++コンパイルを行うためにCode Runnerの設定を行います.
Extension --> Code Runner --> 設定(歯車マーク) --> Extension Setting --> Code-runner:Executor Map --> Edit in settings.json
でsettings.jsonを開きます.

{
    "code-runner.runInTerminal": true,
    "workbench.sideBar.location": "left",
    "editor.suggestSelection": "first",
    "vsintellicode.modify.editor.suggestSelection": "automaticallyOverrodeDefaultValue",
    "runner.languageMap": {
        "cpp": "/Users/ユーザー名/vsc_run/cpp.sh"
    }
}

重要なのは"runner.languageMap"の部分で,使用言語毎に走らせるshell scriptのPathを設定します.
vsc_run/cpp.shはまだ存在しないので,これから作成します.

2.3. Shell scriptの作成

cpp.shを作ります.

$ mkdir ~/vsc_run
$ emacs cpp.sh

cpp.shの一例

#!/bin/sh
file = $1
objfile = `echo $f | sed 's/\.[^\.]*$//'`

g++ -g -o $objfile $file
./$objfile

main.cppをコンパイルしてmainという実行ファイルを作成する,という内容です.
VSCodeのCode Runnerによってこのshell scriptが呼ばれ,コンパイルおよび実行がされます.

2.4. 動作確認

それではVSCodeから実行します.デフォルトではcontrol + option + nで実行されます.(押しにくい) VSCode画面下部のターミナルに,

hello world: 
birthday is 198655

と表示されれば成功です.歳がバレますね.

2.5. stdc++.hを使えるようにする

競プロで便利だと言われるstdc++.hをincludeできるように設定していきます.
先ほどのサンプルでincludeした含め,必要なライブラリのincludeがこれ1つで出来るようになります.

まず,stdc++.hの在処を見つけましょう.

$ cd /usr/local/
$ find . -name "stdc++.h"
./Cellar/gcc/9.2.0_1/include/c++/9.2.0/x86_64-apple-darwin18/bits/stdc++.h

このファイルをコンパイラが認識できる/usr/local/下に配置します.

$ mkdir /usr/local/include/bits
$ cp ./Cellar/gcc/9.2.0/include/c++/9.2.0/x86_64-apple-darwin18/bits/stdc++.h /usr/local/include/bits/

ここまで作業すれば,#include <bits/stdc++.h>でコンパイルが通るようになります.

2.6. C/C++ Intellisenseの設定

コンパイルは通るものの,未だ#include <bits/stdc++.h>に赤破線が出たり,コードの補完が効かない状態にあるそうです.
これを正常に動作させるために,Intellisense機能に/usr/local/include/をpathとして登録し,stdc++.hが認識できるようにします.

Command+Shift+Pでコマンドパレットを開き,"C/Cpp: Edit configurations"を選択する.
jsonファイルのなかの"includePath"の部分に,/usr/local/include/**を追加してpathを登録します.
"compilerPath"も2.2.で設定した/usr/local/bin/g++になっているか確認しましょう.

"configurations": [
    {
        "name": "Mac",
        "includePath": [
            "${workspaceFolder}/**",
            "/usr/local/include/**"
        ],
        "defines": [],
        "macFrameworkPath": [
            "/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/System/Library/Frameworks"
        ],
        "compilerPath": "/usr/local/bin/g++",
        "cStandard": "c11",
        "cppStandard": "c++17",
        "intelliSenseMode": "gcc-x64"
    }
],
"version": 4

}

3. デバッグ

デバッグできる環境作成のためにtasks.json,およびlaunch.jsonの設定を行います.

3.1. tasks.jsonの設定

Command+Shift+Pでコマンドパレットを開き,taskと入力し,選択肢から"Tasks: Configure Default Build Task"を選択します. いくつか選択肢が出てくるので,今回使用するコンパイラに合わせて"C/Cpp: g++ build active file"を選択します.(少々うろ覚え)
以下にtasks.jsonの例を示します.環境変数fileDirname, fileBasenameNoExtensionを使ってあげると,いちいちファイル毎に引数を設定する必要がなくなり楽です.

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558 
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "type": "shell",
            "label": "g++ build active file",
            "command": "/usr/local/bin/g++",
            "args": [
                "-g",
                "${file}",
                "-o",
                "${fileDirname}/${fileBasenameNoExtension}"
            ],
            "options": {
                "cwd": "/usr/local/bin"
            },
            "problemMatcher": [
                "$gcc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            }
        }
    ]
}

3.2. launch.jsonの設定

次にデバッグ設定のlaunch.jsonを作成します. Command+Shift+Pでコマンドパレットを開き,debugと入力し,選択肢から"Debug: Open launch.json"を選択します. 環境の選択は"C++(GDB/LLDB)"を選択.(うろ覚え)
LLDBをデバッガとして使うため,"type"を"lldb"に設定します. "externalConsole"をtrueに設定することで,競技プログラミングで出てくるような外部入力用コンソールが立ち上がるようになります.

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "(lldb) Launch",
            "type": "lldb",  // cppdbgから変更
            "request": "launch",
            "program": "${fileDirname}/${fileBasenameNoExtension}",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": true,
            "MIMode": "lldb"
        }
    ]
}

3.3. デバッグしてみる

確認したい箇所にブレークポイントを貼り,画面左の方にある虫さんマークを選択します.
debugの情報表示画面が出てきたら,F5でdebugを開始しましょう.

しかしここでハマりました. 冒頭で書いたCtrl+option+nでbuildすると何か違うモードに入ってしまうようで,ブレークポイントを設定しても止まってくれませんでした. Shift+command+bで再buildしてあげると正常にdebugを行う事ができました.
そう考えると今回の件ではCodeRunnerを使うメリットはあまりなかったということになりますね.

まとめ

めでたくbuild & debug環境をmacVSCode上で構築する事が出来ました.
これで競技プログラミング含め,Cppの勉強が捗ると思います.

Reference