ゆくゆくは有へと

おかゆ/オカ∃/大鹿有生/彼ノ∅有生 の雑記

パラシェルターの傘開き時間とインク消費の関係性について

要約

傘を1秒開くと、メイン弾約4発分(メイン効率無積みなら20%)消費する。

動機

発端はこれ

傘が開いてる間、インク漏れてるってことがあんまり知られてなかったことにも驚き(みんなほんま使ったことないんやな)だったけど、

傘がオープン中のインク消費量がググってもでてこないことのほうが驚きだったよ!とっくに調べられてると思った(情報あったら教えてください)

というわけで、傘開き時間とインク消費の関係性について調べました。

調べ方

  1. 傘が開いた瞬間に、僕の人差し指がストップウォッチのスタートを押す
  2. 適当な時間経ったのち、僕の人差し指がストップウォッチの停止を押すと同時に傘を開くのをやめる
  3. 残りのインクを、すべてメイン弾で消費し、どれだけの弾数が必要だったかを数える(タンクに残った微妙なインク量は目視で適当に加算)

キャプチャーボードもないので、測定方法があまりにもガバガバですが、目安くらいにはなるかと思います。

なお、今回はメイン効率無積みで行っています。

結果

42回ほど測定しました。どうやら直線関係のようです。一部、階段のようになっていますが、これはタンク内部のインク量を正確に見積もれてないからだと思います。

f:id:waraby0ginger:20171111060541p:plain
傘開き時間(秒)とインク消費量(メイン弾換算)の関係

直線関係なので、比例式でフィッティングさせたところ、y = 3.85 x 程度となり、それらしい値が求められました。R2見ても、いい感じでしょう。

考察と感想

まず事前情報です:

  • メイン効率無積みの場合、傘のメイン弾は20発撃てる(つまり、1弾あたりのインク消費量は5%)
  • 傘がパージするまでフルで開いた場合、約1.65秒で、メイン弾換算にして6発分になる
  • インク効率をいくつか振ってフル開きの場合のみ試してみたが、どの状態でも開いてからパージするまでに消費するインク量は、そのときのメイン弾換算で6発

精度について。他の検証などを見てても、パラメータは大体小数点以下1桁くらいまでで設定されてるように思います。ただ今回は測定精度がそんなによくないことも踏まえると、「ほぼ4発分」くらいだと考えるのが一番無難かなと思います。

その場合、パージまで傘を開いたときを計算すると、4 * 1.65 = 6.6となり、1発分ズレますが、それなりに当たっています。

さて、関連で2つ動画を載せます:

まず1つ目について。これくらいの一瞬よりは少し長めくらい開くというのを繰り返した場合、0.25 秒くらいだとして、20 / (1 + 3.85 * 0.25) = 10.19 発撃てることになります。

次に2つ目。これくらいのスパンで開いた場合、0.1秒くらいだとして、20 / (1 + 3.85 * 0.1) = 14.44 発程度撃てる計算になります。大体近い値にはなってるかなという感じです。

話は変わって、メイン弾の連射速度は約2発/秒なので、1秒で4発分消費する傘開きは、普通に連射する場合の2倍のインク消費速度ということになります。結構エグい。

「ポイズンミストを投げると傘は溶ける」を発端とした検証だったので、できればそこまでしたかったですが…やむなし。誰かやってくれませんか?ポイズンミストでインク消費量がどれだけ倍増するのかが調べても出てこなかったんですが、2倍ってことはないよね…?それでも仮に1.5倍消費量増えると考えると、インク効率無積みでメイン弾 7.5%、傘開き 30%/秒となり、いや~~~シビアっす。30%て。

補足

1.65秒で6発分、しかも原点通る直線に従うってことなら、これだけでも値は出せて、6 / 1.65 = 3.64 くらいです。実は 3.75 とかが実際のパラメータなのかしら。

Splatoon2のギアパワー計算式をもう少しキレイにする

こんにちは、スプラトゥーン2最高に楽しいですね。

ご存知の通り、ギアパワーの計算式ってのがありますが:

[1] ギアパワー検証 - Splatoon2 - スプラトゥーン2 攻略&検証 Wiki*

[2] ギアパワー効果測定【スプラトゥーン2調査 / Splatoon2】 – なんどろいどの開発記録

微妙に値が追いづらいので、もう少しエルゴノミックな形にしようとしました。

概要

xをギアパワー値、[MAIN]*10 + [SUB]*3とすると、

p = (0.99*x - (0.09*x)^2)

というのがギアパワーにほぼ普遍的に出てくる項で、これになんかしらの係数をかけたものが減少率・増加率に相当します。

ただ、このpなんですが、x = 57のとき p = 30.1131 くらいになってあまり覚えやすい値にならないので、基本値として、これを30で割った

X = (0.99*x - (0.09*x)^2) / 30

を定義することにします。そうすると、Xはギアパワー全振りでほぼ 1 になるので分かりやすいです。

で、さっきも言った通り、このXになんかしらの係数をかけたものが減少率・増加率に相当します。その係数を補正係数と呼んで、Rで表すと、

A = RX

となります。A は実際の減少率・増加率に相当します。

結局、基本となる値になんかしらの係数をかけるという点では変わらないんですが、このRの優秀なところは、この値自体が増加率・減少率の「最大」を意味するというところです。

例:インク効率(メイン)

例がないとよくわからないので、[1]のインク効率(メイン)のグループ1の場合を計算してみます。

[1]の計算式だと、基本となる値にかける係数は 1/60 であり、最大軽減率である 50% とはほぼ関連のない値になっています。

一方で、ここで提案した方法だと、R = 30 / 60 = 0.5 となって、これはまさに最大軽減率 50% のことを意味します。

最終的に、実際のインク効率 Z は、デフォルト値を D とすると、

Z = D * (1 - RX)

で求められ、各パラメータ R, X, D が意味を解しやすいものとなっていてエルゴノミックです!

有意味なパラメータ

結局、この方法だと

  • R: 変化率の最大値
  • X: 最大変化率に対する割合
  • D: デフォルト値

について把握しておけばOKということになります。

従来の、実際のステ変化とあまり関連づけて覚えられない係数より断然覚えやすいかと思います。

X の変化具合

とはいえ、より直観的に把握するためには、X とギアパワー値の対応関係についても覚えておく必要がありますが、この関係は二次関数的であって、そんな簡単に覚えておけるものでもありません。計算すればいいんですが、面倒ですしね!

なんで、主要な各点の値を把握してればいいんじゃないかと思います。

x = 0, 10, 20, 30, 39, 48, 57 に対して、それぞれ

X = 0, 0.30, 0.55, 0.75, 0.88, 0.96, 1.00

となります。%表示にするなら、30%, 55%, 75%, 88%, 96%, 100% ですね。

さっきのインク効率(メイン)の例を再び持ってくると、グループ1の武器で、メイン1つ付いていれば、R = 0.5X = 0.3 すなわち、最大軽減率 50% の 3割、15% くらいのインク効率アップが見込めるということになります。

ヒト速の通常歩きもR = 0.5 なので、メイン2個つければ50%の55%、だいたい27.5%の速度アップが見込めます。

X の線形補間

上の主要点の値を把握しておけば、これの線形補間で間の値も概算できます。

たとえば、x = 16におけるX(%) は、

X(%) = 30 + 6 * (55 - 30)/(20 - 10) = 30 + 6 * 25/10 = 45

となります。厳密な式に従えば、X = 45.9 なので、ほぼ値を捉えられています。

まとめ

コンピュータに計算してもらったほうがはやい

Pythonのスライシングの速度に気をつけて

今日のAtcoderのコンテスト(ABC)で死んだので自戒を込めて。

Pythonのスライシングが遅い話

Pythonのスライシングってリストコピーされるんでしたね……。通りで遅いわけ……。

で、スライシング使わずにdelしたのが次の2つ

Submission #1564678 - AtCoder Beginner Contest 072

Submission #1564698 - AtCoder Beginner Contest 072

前者はリストの前から削除していくやり方。

ところでPythonでは挿入や削除は後ろからしたほうが速いので、後ろから削除していくやり方が後者。

実行速度が5倍に…(´・_・)(´・_・)

いやあ、インプレースな操作(とリストの得意なやり方)って大事ですね…(´・_・`)

Disjoint set を勉強した AOJ #2512222 - Python編

Disjoint Set: Union Find Tree | Aizu Online Judge

AOJの解説とは異なり、root が自身へのポインタではなく、その木の集合を持つようにしてある。

当たり前ながら、破壊的更新ができるなら冗長な構造ですけど、ちょっと先のことも踏まえてこうした。

Pythonの集合はたしかハッシュセットなので、そこまで極端に速度落ちないことを期待する。

あと、競プロにありがちなデータ入力が毎回打つのダルすぎて、Stdinクラスに関数をまとめた。

パターンマッチ使いて~~~~~~~~~~~~~~~~~~~~~~~

Aizu #2512222

勉強がてら二分木をつくった回

純粋関数型データ構造

純粋関数型データ構造

今これ↑を読んでて、二分木の実装を練習がてらRustでしてみようと思ったのがきっかけ。

BTreeを実装しようとした

下の木をムーブしないようにmatchref 付けまわるのがたいへんでした(小並感)

やってて気づいたけど、この本にあるように、下の木を共有するの、Rustじゃデフォでできんや~ん

純粋だからこそ共有できるんやね(あたりまえのことをじっかんする)。

〈じゅんすいかんすう型でーたこうぞう〉の二分木をつくるなら、Rc使えばよさそうですね:

純粋な二分木

たぶん

追記 8/24 3:00

RustのBTreeMapはどうなってるんだろうと思ってソース見てみた:

doc.rust-lang.org

んんっ、なんだこのnodeモジュールとやらはっ ソースどこにあるんだろわからん

指定文字消すのどうすればいいんや

abc002.contest.atcoder.jp

“The Rust Programming Language” の第二版を最終章除いて読み終わったので、Atcoderでぽちぽちコード書く練習してるんだけど、 API全然知らないのでもうわっかんな~い(へらへら)

ありがたい………

qiita.com

use std::io;

fn main() {
    let mut buffer = String::new();
    io::stdin().read_line(&mut buffer).unwrap();

    let mut word = buffer.trim().to_string();
    for ch in "aeiou".chars() {
        word = word.split(ch).collect::<Vec<_>>().concat();
    }
    println!("{}", word);
}

効率的な文字の消し方が分からんかったので、各母音字でsplitして集めてを繰り返すという微妙なやりかたをした。

調べてて気づいたが、collect::<Vec<_>>().concat() でなく、collect::<String>()にしたほうが賢いね。
collectFromIteratorトレイトのメソッドで、charイテレータStringにコレクトできるので。

というようなことを考えてると、for文消したくなるね。

use std::io;

fn main() {
    let mut buffer = String::new();
    io::stdin().read_line(&mut buffer).unwrap();

    let word = buffer.trim().to_string();
    let word = "aeiou".chars().fold(word, |w, c| w.split(c).collect::<String>());
    println!("{}", word);
}

畳み込めばいいよね!ってことで畳み込んだ。

もう少し素朴なやりかた。containsStringpushをおぼえた。

use std::io;

fn main() {
    let mut buffer = String::new();
    io::stdin().read_line(&mut buffer).unwrap();

    let mut word = String::new();
    for c in buffer.trim().chars() {
        if !['a', 'e', 'i', 'o', 'u'].contains(&c) {
            word.push(c)
        }
    }

    println!("{}", word);
}

&strcharの配列なりスライスではないので、if !['a', 'e', 'i', 'o', 'u'].contains(&c)と書かないといけないのがPythonマンのおかゆには厳しさがある。

if !"aeiou".chars().collect<Vec<_>>.contains(&c)とすればいけるけど。もっと簡単な書き方あるのかな~。

と色々書いてて思いついたけど、filter使えばいいのでは……?

use std::io;

fn main() {
    let mut buffer = String::new();
    io::stdin().read_line(&mut buffer).unwrap();

    let word: String = buffer.trim()
                             .chars()
                             .filter(|c| !['a', 'e', 'i', 'o', 'u'].contains(&c))
                             .collect();
    println!("{}", word);
}

やっぱイテレータ触るならチェーンやで……!といわんばかりのチェーン 単語を文字のイテレータにして、子音だけパスしてコレクト。リストの内包表記こそ使えないものの、それにいちばん近いのは今までの中ならこれかな?

Pythonでなら、多分こう書くもんな:

word = "".join([c for c in word if c not in "aeiou"])
print(word)

f:id:waraby0ginger:20170807012446j:plain

追記(2017/8/7/20:05):replace あるやんけ

use std::io;

fn main() {
    let mut buffer = String::new();
    io::stdin().read_line(&mut buffer).unwrap();

    let word: String = buffer.trim()
                             .replace(&['a', 'e', 'i', 'o', 'u'][..], "");
    println!("{}", word);
}

あれからstrのこと調べてると、スライスとstrcontains, starts_with, ends_with, find, split系の引数が違うらしいことを知った。

strでは、要素1つの代わりにパターン(Patternトレイトを実装している型)を取るらしい:

  • &String
  • &[char]
  • char
  • FnMut(char) -> bool
  • &&str
  • &str

文字1文字はもちろんのこと、部分文字列やら、char.is_numericといった関数も与えれるので、結構柔軟にパターンが作れるらしい。

特に、&[char]論理和(って言い方であってんのか)なので便利…。

ちなみにおかゆは .replace(&['a', 'e', 'i', 'o', 'u'], "") ってして「&[char; 5]はダメっぷ~~~~ww」ってコンパイラに言われてキレてたんですけど、
ジェネリクスのトレイトバウンドの箇所では諸々の型強制(今回だとunsize coercion)が効かないの忘れてた(てへぺろ

Coercions -

こういう場面で型強制効かないのって安全のため?なんだろうけど、トレイトバウンドに使うときに融通の効くトレイトを作るのって結構大変そうだね。

人工言語学Wikiの「関与原理」へのコメント

ja.conlinguistics.wikia.com

コメントがあって、コメントしようとしたらなぜかこの記事にだけログインできない謎に遭遇してコメントできなかったので、ここに書いておきます。


はじめまして、おかゆです。

Yuhrさんの「格標示に比べれば関与は暗黙的ですが、そもそも、発話されている(言語形式としてそこに存在している)というだけでとんでもない明示と言えるでしょう。」という説明のほうが、本記事よりも関与原理についてよりよく的確に説明しているように感じます。関与原理のエッセンスというのは、言語形式としてそこに存在することの甚大な有標性だと思います。

本記事では、「関与原理」は意味役割の理論の中で説明しようとされていますが、実のところ、関与原理のいう「関与」は種々の意味役割よりも根本・原始にあると思います。

なぜ意味役割と同じ領域の中で「関与原理」を説明しようとしたのか、その動機はちょっと失念してしまいました。ただ、格体系のデザインの話になると、(特に工学言語の分野だとありがちですが)意味役割を格の形式として網羅的に分離しきりたいだとか、動詞のもつ意味役割の体系を完全に明示したいというような、意味役割体系における網羅性・完全性の強迫に駆られることがしばしばありますが、そもそも意味役割云々以前の問題として「言及したい対象について、そこで言い表している」という事実にはたいへん大きな価値があって、多くの語りたいことにとっては、その「言い表しによる有標化」だけで(意味役割を表示することなく)十分なんじゃないか、というようなことを考えて私は「関与原理」などと言い始めたような気がします(そうであれば、意味役割と紐付けて関与原理を説明しようとした動機にもある程度察しがつきます)。「関与原理」自体は「話者は、言いたいことを言い表している(あるいは言い表そうとできる)」というような至極当たり前のようなことに名前をつけただけに過ぎないと考えることもできます(なので私は「原理」と呼んでいるわけですが)。

拙い返事ですみませんが、これで。