Slide 1

Slide 1 text

Goで競プロやってみたので辛 いポイントを言っていく ogataso

Slide 2

Slide 2 text

競技プログラミングとは ● 数学パズルみたいな問題が与えられるので、答えを算出 する十分高速なコードを書くゲーム ● 日本だとAtCoderが人気 ● 言語はC++, Python, Rustなどが人気。 Goでやってる人はあんまりいない

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

今日発表すること Go言語で競プロやってみて苦労したこと、対処法 ● 標準入力がちょい面倒 ● stringがイミュータブル ● ハッシュマップしかない ● bigintが使いにくい C++でそこそこやった経験があるので、 比較しつつ紹介していきます! 知らなかったことが一つでもあればヨシ

Slide 5

Slide 5 text

標準入力がちょい面倒 C++の場合は簡単 int a; string s; float f; cin >> a >> s >> f;

Slide 6

Slide 6 text

標準入力がちょい面倒 Goの場合も簡単(?) var a int var s string var f float64 fmt.Scan(&a, &s, &f)

Slide 7

Slide 7 text

標準入力がちょい面倒 Goの場合も簡単(?) fmt.Scanはバッファを使わないので遅い。TLEするかも... var a int var s string var f float64 fmt.Scan(&a, &s, &f)

Slide 8

Slide 8 text

Go C++ var sc = bufio.NewScanner(os.Stdin) var wr = bufio.NewWriter(os.Stdout) defer wr.Flush() sc.Split(bufio.ScanWords) sc.Buffer([]byte{}, math.MaxInt32) var n int if sc.Scan() { n, _ = strconv.Atoi(sc.Text()) fmt.Fprintf(wr, "Scanned integer: %d\n", n) }

Slide 9

Slide 9 text

Go C++ var sc = bufio.NewScanner(os.Stdin) var wr = bufio.NewWriter(os.Stdout) defer wr.Flush() sc.Split(bufio.ScanWords) sc.Buffer([]byte{}, math.MaxInt32) var n int if sc.Scan() { n, _ = strconv.Atoi(sc.Text()) fmt.Fprintf(wr, "Scanned integer: %d\n", n) } bufioを使って、標準入力からのデータを 一度に読み込み、バッファしておくように する

Slide 10

Slide 10 text

Go C++ var sc = bufio.NewScanner(os.Stdin) var wr = bufio.NewWriter(os.Stdout) defer wr.Flush() sc.Split(bufio.ScanWords) sc.Buffer([]byte{}, math.MaxInt32) var n int if sc.Scan() { n, _ = strconv.Atoi(sc.Text()) fmt.Fprintf(wr, "Scanned integer: %d\n", n) } bufioを使って、標準入力からのデータを 一度に読み込み、バッファしておくように する sc.Scan, sc.Text()で空白(改行)区切り の文字列を読み込み、型変換

Slide 11

Slide 11 text

Go func getI() int { sc.Scan() i, _ := strconv.Atoi(sc.Text()) return i } func solve() { n := getI() fmt.Fprintln(wr, n) } 実際の利用では関数にして記述量を減らせばそれほど苦ではない

Slide 12

Slide 12 text

Stringがイミュータブル C++の場合は簡単 Goの場合これだとコンパイルエラー string str = "mirratib"; str[7] = 'v'; str := "mirratib"; str[7] = 'v';

Slide 13

Slide 13 text

Stringがイミュータブル 仕方がないので[]byteに変換する もしくは最初からbytesで入力を受け取る str := "mirratib" bytes := []byte(str) bytes[7] = 'v' fmt.Fprintln(wr, string(bytes))

Slide 14

Slide 14 text

Goはハッシュマップしかない C++ではstd::map(ツリーマップ)とstd::unordered_map(ハッシュ マップ)がある ツリーマップ ハッシュマップ 平均計算量 O(logN) O(1) 要素の順序 常にソートされる ソートされない キーの二分探索機能 有り 無し

Slide 15

Slide 15 text

キーの二分探索とは map mp { {1, "one"}, {2, "two"}, {5, "five"}, {10, "ten"} }; auto it = mp.upper_bound(2); // 2より大きい最初のキーの要素取得 cout << it->first << " => " << it->second << endl; // 5 => five 競プロではこの操作は典型テクニック

Slide 16

Slide 16 text

Goでツリーマップを使うには AtCoderではgithub.com/liyue201/gostl/ds/map のツリーマップ が使用可能 tree := rbtree.New[int, string](comparator.IntComparator) tree.Insert(1, "one") tree.Insert(2, "two") tree.Insert(5, "five") tree.Insert(10, "ten") node := tree.FindUpperBoundNode(2) fmt.Fprintln(wr, node.Key(), node.Value()) // 5 five

Slide 17

Slide 17 text

bigintが使いにくい uint64を超える数値を扱うことがたまにある ● PythonやRubyはオーバーフロー気にしなくて良い ● C++はboostライブラリのcpp_intを利用できる #include namespace mp = boost::multiprecision; int main() { mp::cpp_int x = 1; for (unsigned int i = 1; i <= 100; ++i) x *= i;

Slide 18

Slide 18 text

bigintが使いにくい Goでは標準ライブラリmath/bigを使える import "math/big" func main() { x := big.NewInt(1) for i := int64(1); i <= 100; i++ { x.Mul(x, big.NewInt(i)) // x *= i }

Slide 19

Slide 19 text

bigintが使いにくい import "math/big" func main() { x := big.NewInt(1) for i := int64(1); i <= 100; i++ { x.Mul(x, big.NewInt(i)) // x *= i } ● 四則演算にAdd, Mulなどを使う(演算 子オーバーロードないので) ● 演算実行時、bigintに型変換

Slide 20

Slide 20 text

まとめ ● 標準入力ではbufioを使おう ● string->[]byte->stringみたいな型変換が頻発する ● ツリーマップは外部ライブラリ頼り ● bigintちょっと使いずらい 個人的には、C++が一番だが、Goも悪くないと思った