LoginSignup
0
0

More than 5 years have passed since last update.

オフラインリアルタイムどう書く E31 の実装例( Go, C++ )

Posted at

問題の名前 : ぐるぐる数
問題 : http://nabetani.sakura.ne.jp/hena/orde31rotnum/
実装リンク集 : https://qiita.com/Nabetani/items/d139d5ef70aa23c2d038

次回のイベントは 4/6
see https://yhpg.doorkeeper.jp/events/88379

で。

ナイーブな実装を Go と C++ で。

Go

go
package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "os"
    "strconv"
    "strings"
    "time"
)

func isGuru(b, i int32) bool {
    prev := int32(-1)
    for {
        num := i % b
        if 0 <= prev && prev != num && prev != (num+1)%b {
            return false
        }
        i = (i - num) / b
        if i == 0 {
            return true
        }
        prev = num
    }
}

func impl(b, x, y int32) int32 {
    c := int32(0)
    for i := x; i <= y; i++ {
        if isGuru(b, i) {
            c++
        }
    }
    return c
}

func solveSlow(src string) string {
    vals := strings.Split(src, ",")
    b, e0 := strconv.Atoi(vals[0])
    x, e1 := strconv.ParseInt(vals[1], b, 32)
    y, e2 := strconv.ParseInt(vals[2], b, 32)
    if e0 != nil || e1 != nil || e2 != nil {
        panic(fmt.Sprint(e0, e1, e2))
    }
    count := impl(int32(b), int32(x), int32(y))
    return fmt.Sprint(count)
}

func bytesFromFile(fn string) []byte {
    f, err := os.Open(fn)
    if err != nil {
        panic(err)
    }
    defer f.Close()
    b, err := ioutil.ReadAll(f)
    if err != nil {
        panic(err)
    }
    return b
}

type testDataType struct {
    Number   int    `json:"number"`
    Src      string `json:"src"`
    Expected string `json:"expected"`
}

type dataType struct {
    EventID  string         `json:"event_id"`
    EventURL string         `json:"event_url"`
    TestData []testDataType `json:"test_data"`
}

func runTest(list []testDataType) {
    for _, t := range list {
        start := time.Now()
        actual := solveSlow(t.Src)
        tick := time.Now().Sub(start).Seconds()
        result := func() string {
            if actual == t.Expected {
                return "ok"
            }
            return "***NG***"
        }()
        fmt.Printf("%2d:%s solve(%q)=%q, want %q ( %.3f sec )\n", t.Number, result, t.Src, actual, t.Expected, tick)
    }
}

func main() {
    data := dataType{}
    json.Unmarshal(bytesFromFile(os.Args[1]), &data)
    start := time.Now()
    runTest(data.TestData)
    tick := time.Now().Sub(start).Seconds()
    fmt.Printf("total : %.2f sec\n", tick)
}

実行は go run slow.go data.json みたいな感じで。

随所で int32 と書いているのは、int よりもたぶん速いから。手元の環境では int は 64bit なので、落ちるコードに差が出てくる。

この実装で、手元の環境で 75秒ぐらい。
goルーチンを活用すれば半分ぐらいにはなるはずだけど、何もしていない。

C++

こちらは、cielavenir さんの記事を参考にして、OpenMP を入れてみた。

c++17
// clang++ -O2 -std=c++17 -Wall -Xpreprocessor -fopenmp -lomp  3.cpp

#include <iomanip>
#include <iostream>
#include <numeric>
#include <regex>
#include <sstream>
#include <string>

int is_guru(int b, int i) {
  auto prev{-1};
  for (;;) {
    auto num = i % b;
    if (0 <= prev && prev != num && prev != (num + 1) % b) {
      return 0;
    }
    i = (i - num) / b;
    if (i == 0) {
      return 1;
    }
    prev = num;
  }
}

std::string solve(std::string const &src) {
  auto regex = std::regex(R"(^([^\,]+)\,([^\,]+)\,([^\,]+))");
  std::smatch m;
  std::regex_match(src, m, regex);
  auto b = std::stoi(m[1]);
  auto x = std::strtol(m[2].str().c_str(), nullptr, b);
  auto y = std::strtol(m[3].str().c_str(), nullptr, b);
  int count = 0;
#pragma omp parallel for reduction(+ : count)
  for (int i = x; i <= y; ++i) {
    count += is_guru(b, i);
  }
  return std::to_string(count);
}

void test(std::string const &src, std::string const &expected) {
  auto actual{solve(src)};
  auto okay{actual == expected};
  std::cout << (okay ? "ok" : "**NG**") << " " << src << " " << actual << " "
            << expected << std::endl;
}

int main() {
  /*0*/ test("4,1313,3012", "12");
  /*1*/ test("10,100,110", "0");
  /*2*/ test("36,zoo,zyz", "0");
  /*3*/ test("4,1300000,2222221", "0");
  // 中略
  /*68*/ test("2,101001011010110000110101,110101110001110110110101", "3240321");
}

Go の実装とロジックは同じ。
というか、Go の実装を移植しただけ。
入力のパースで正規表現を使ってみた。std::split とかあると嬉しいんだけどなぁ。
「中略」のところを略さないで実行して、手元のマシンで、10秒弱。OpenMP 様々。

0
0
0

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
0
0