金魚亭日常

読書,ガジェット,競技プログラミング

AtCoder Chokudai Speed Run #001

AtCoder の新しいウェブサイトのテストとして開催されたコンテスト.

12問 速解き.

A 最大値

数列の最大値を出力

max()

B 和

数列の和を出力

sum()

C カンマ区切り

数列をカンマ区切りで出力

join()

D ソート

数列をソートして半角スペース区切りで出力

sort(), join()

E 1は何番目?

index()

F 見える数

「数列 a に含まれる整数のうち、1≤j<i≤N を満たす任意の j において、 aj<ai を満たすような i がいくつあるか出力しなさい。」という問題.

0番目は必ず条件を満たすので,まず一つ.

あとは,数列を1からn-1まで順番に見ていって,自分より左の最大値より大きいかを判定する.

G あまり

読み込んだ数列を連結した数値を 1,000,000,007 で割る.

H LIS

数列から好きなだけ要素を取り除いて単調増加数列を作る時の,最大の長さを求める.

二分探索する.

I 和がNの区間

連続区間の和がNになるものの数.

左から順番に和を求めて行って,和がNを超えたら次に行く.

J 転倒数

バブルソートの交換回数.

転倒数,と来たら,BinaryIndexedTree (BIT) , とアリ本にも書いてあるのだけど,このスライドがわかりやすかった.

www.slideshare.net

K 辞書順で何番目?

こういう操作で数えられるが,よく見ると BIT が使える.

f:id:what_alnk:20170730140441p:plain

L辞書順で何番目?

N回交換してソートできるか.

なので,J 問題と同じくバブルソートの交換回数を求めて,Nと比較する.

これ,Nの方が多い場合,同じやつの交換を繰り返せばいいので,差が偶数かどうかで判定できることはわかるけど,Nの方が少ない場合はこれでいいのだろうか,と思った.

AtCoder Chokudai Speed Run #001

R の .Renviron の ${R_HOME}

これの話

etc/Rcmd_environ をコピーして .Renviron を作る場合,

R_SHARE_DIR=${R_HOME}/share

と書かれているが,この ${R_HOME} は何になっているのか,という話.

Rを起動した後に Sys.getenv() して確かめてみると,

C:/R/R-3.3.2

といったRのインストーディレクトリになっているが,.Renviron を解釈する段階では,

C:\R\R-3.3.2

になっているらしくて,

R_SHARE_DIR=${R_HOME}/share

R_SHARE_DIR=C:RR-3.3.2/share

になってしまうらしい.

これが起こるのは,R.exe だけで,RStudioでは起こらない.

レジストリHKEY_LOCAL_MACHINE\SOFTWARE\R-core\R\3.3.2\InstallPath (32bitの場合)を / 区切りにするとこの問題は起こらないらしいので,R_HOMEの値はもともとこのレジストリの値から来ているらしい.

回避策としては,R_HOME の値を .Renviron/区切りで書けばよい.

Go の ファイルから読み込んだ Unicode コードポイント文字列の Unquote

これの話

str := "\u3053\u3093\u306b\u3061\u306f\uff0c\u4e16\u754c\uff01"
fmt.Println(str)
// こんにちは,世界!

だが,

str := `\u3053\u3093\u306b\u3061\u306f\uff0c\u4e16\u754c\uff01`
fmt.Println(str)
// \u3053\u3093\u306b\u3061\u306f\uff0c\u4e16\u754c\uff01

なので,ダブルクオートで囲ってから,strconv.Unquote() を使うと,

str := `\u3053\u3093\u306b\u3061\u306f\uff0c\u4e16\u754c\uff01`
unquoted, _ := strconv.Unquote(`"` + str + `"`)
fmt.Println(str)
// こんにちは,世界!

となる.

ファイルから読む場合,

f, _ := ioutil.ReadFile("text.txt")
str := strings.TrimRight(string(f), "\n")
unquoted, _ := strconv.Unquote(`"` + str + `"`)

という順番.

StringIO 的にやるには,

f := bytes.NewBufferString(`\u3053\u3093\u306b\u3061\u306f\uff0c\u4e16\u754c\uff01`).Bytes()
str := strings.TrimRight(string(f), "\n")
unquoted, _ := strconv.Unquote(`"` + str + `"`)

でいいと思われる.

`dplyr::case_when()` の中身を先にlistに入れておく

ドキュメントには

patterns <- list(
  TRUE ~ as.character(x),
  x %%  5 == 0 ~ "fizz",
  x %%  7 == 0 ~ "buzz",
  x %% 35 == 0 ~ "fizz buzz"
)
case_when(!!! patterns)

みたいに書いてあるので, list に入れればいいかと思ったけど,少し違った.

dfdf.cond という data.frame があったときに,df を 列 numdf.condfrom から to までに入るか,でグループ分けして,グループを 列group に入れる,ということを考える.

df <- data.frame(num = c(1:14))
df
#>    num
#> 1    1
#> 2    2
#> 3    3
#> 4    4
#> 5    5
#> 6    6
#> 7    7
#> 8    8
#> 9    9
#> 10  10
#> 11  11
#> 12  12
#> 13  13
#> 14  14

df.cond <- data.frame(from = c(1, 5, 10), to = c(4, 9, 14), val = c("A", "B", 
  "C"), stringsAsFactors = FALSE)
df.cond
#>   from to val
#> 1    1  4   A
#> 2    5  9   B
#> 3   10 14   C

まとめると,

  • list ではなく, exprs を使う
    • unevaluated expression であることが必要
  • 各条件については,
    • 条件全体は exprcase_when のときに評価
    • df.cond に関する部分は UQ で 先に評価しておく

つまり,

library(dplyr)
library(rlang)
patterns <- exprs()
for (i in 1:nrow(df.cond)) {
  patterns <- append(patterns, expr(.data[["num"]] >= UQ(df.cond$from[i]) & 
    .data[["num"]] <= UQ(df.cond$to[i]) ~ UQ(df.cond$val[i])))
}
patterns
#> [[1]]
#> .data[["num"]] >= 1 & .data[["num"]] <= 4 ~ "A"
#> 
#> [[2]]
#> .data[["num"]] >= 5 & .data[["num"]] <= 9 ~ "B"
#> 
#> [[3]]
#> .data[["num"]] >= 10 & .data[["num"]] <= 14 ~ "C"

というようになり,

df <- df %>% mutate(group = case_when(!!!patterns))

#>    num group
#> 1    1     A
#> 2    2     A
#> 3    3     A
#> 4    4     A
#> 5    5     B
#> 6    6     B
#> 7    7     B
#> 8    8     B
#> 9    9     B
#> 10  10     C
#> 11  11     C
#> 12  12     C
#> 13  13     C
#> 14  14     C

という感じになる.

ちなみに,reprex::reprex() では case_when(!(!(!patterns))) となり,

#> Error in mutate_impl(.data, dots): Evaluation error: invalid argument type.

というエラーになった.

Twitter の リストがおかしかったので作り直した

Web版を使っている.

“Tech2” という非公開リストがあるのだけど,いつの日からか “Tech3” という名前でも表示されるようになってしまった.

つまり,

https://twitter.com/user_name/lists/tech2

でも

https://twitter.com/user_name/lists/tech3

でもアクセス可能で,リスト一覧でもどちらかが表示される.

一覧に表示されるのはどちらか一方だが,アドレスを直接打てば常に両方にアクセスできて,同じタイムラインが表示される(not found でも5回ぐらいリロードすればアクセスできる).

サポートに連絡したが音沙汰がないので,別の名前でリストを作り直した.

“Tech2” から新しいリストに移したのだが,リストのメンバーの中には “Tech3” に属しているアカウントもあった. この時,"Tech2" は表示されていない.

昔,"Tech3" というリストを “Tech2” に統合したことが原因だと思うけど,なぜ今更おかしくなったのだろう. そして,内部実装どうなっているのだろう.

ちなみに,リストは同じ名前のものを作ることができて,表示名は同じになるが, URL の方は自動的に末尾に 1 とか 2 とかが付く.

R でコマンドライン引数を扱う

これ

stackoverflow.com

に回答したときに,コマンドライン引数の扱いについて調べた.

  • スペースを含む引数は "--arg=hoge fuga" のように全体をクオートで囲う
    • --args='hoge fuga とするとスペースで切られる
  • optparseを使うと Python っぽく書ける
    • 自作関数を作らなくていいし,変な入力もチェックできる
    • コマンドライン引数をとってくる部分は commandArgs() なので,スペースを含む引数の指定の仕方は同じ

github.com

  • 他にもいくつかパッケージがあるようだ

optparse を使わない

質問した人はこんな感じにコマンドライン引数を扱っていた.

  • args_test.r
args <- commandArgs(trailingOnly=T)
parseArgs <- function(x) strsplit(sub("^--", "", x), "=")
print(parseArgs(args))

実行

> r --slave --file=args_test.r --args --title=TITLE "--author=AUTHOR NAME"

# [[1]]
# [1] "title" "TITLE"

# [[2]]
# [1] "author"      "AUTHOR NAME"

これでもエラーにならない

> r --slave --file=args_test.r --args --title=TITLE --author='AUTHOR NAME'
# [[1]]
# [1] "title" "TITLE"
# 
# [[2]]
# [1] "author"  "'AUTHOR"
# 
# [[3]]
# [1] "NAME'"

optparse を使う

  • args_test2.r
suppressPackageStartupMessages(library("optparse"))
parser <- OptionParser()
parser <- add_option(parser, c("--title"), action="store", default="TITLE", help="Title")
parser <- add_option(parser, c("--author"), action="store", default="AUTHOR NAME", help="Author")
print(parse_args(parser))

実行

  • --help が付く
> r --slave --file=args_test2.r --args --title=TITLE "--author=AUTHOR NAME"

# package 'optparse' was built under R version 3.3.3
# $help
# [1] FALSE
# 
# $title
# [1] "TITLE"
# 
# $author
# [1] "AUTHOR NAME"

これはエラー

> r --slave --file=args_test2.r --args --title=TITLE --author='AUTHOR NAME'

# Error in getopt(spec = spec, opt = args) :
#   "NAME'" is not a valid option, or does not support an argument
# Calls: print -> parse_args -> getopt
# Execution halted