月別アーカイブ: 2020年3月

Power BIでLOOKUPVALUEを使い、同じテーブルの別の列を参照する

(2018年2月24日にQiitaにて執筆した記事をこちらに転載しています。)

以下のようなデータがあるとします。Filesと名付けます。

f:id:rintaromasuda:20200327165301j:plain

各行のParentIdはそのレコードの親となるレコードのIDを指しています。つまりこのデータは、自分自身を参照しています。

この各レコードに親レコードの名前を追加したいとします。そうすれば例えば、この親レコードの名前を使ってグループ化することなど出来て便利です。もちろんParentIdでもグループ化出来ますが、可読性が落ちてしまいます。

この場合LOOKUPVALUEを使って、次のように新しい列(カラム)を定義します。

ParentName = LOOKUPVALUE(Files[Name], Files[ID], Files[ParentId])

すると以下のようになります。

f:id:rintaromasuda:20200327165331j:plain

ParentIdに準じたNameが表示されました。

3 ÷ 4 ≡ 9 mod 11を計算するのに必要なモジュラ逆数を理解する

(2017年10月17日にQiitaにて執筆した記事をこちらに転載しています。)

経緯

暗号通貨の技術に興味を持っていまして、勉強の為に1からビットコインアドレスを自分で作ってみようと思ったのですが、いきなり躓いたところがあったので共有したいと思いました。

躓いたところ

ビットコインアドレスを作る為の最初の手順は秘密鍵から公開鍵を作ることです。

公開鍵を作る為には楕円曲線上での加法、乗法を行う必要があります(この細かい内容については別記事にしたいと思います。)

公開鍵の作成手順を実装する為にビットコインニュースのこちらのページが非常に参考になりそうだったので読んでいたのですが、突然

def inv_mod(a, p = cP):

という関数が出てきたところで躓いてしまいました。このinv_mod()とは何者で何を計算しているのでしょうか?

inv_mod()とは何者で何を計算しているのか?

多分”inverse modular”かなんかの略だろうという推測のもと検索した結果、この世にはモジュラ逆数なる概念があることを知りました。

モジュラ逆数 – Wikipedia

このWikipediaページや関連する情報を読んで、inv_mod()がモジュラ逆数を計算しているものだということも分かりましたし、モジュラ逆数を計算する方法も分かりました。

しかしそれでも、楕円曲線上での加法や乗法を行うとき、なぜモジュラ逆数を計算する必要があるのかがまったく分かりませんでした。

モジュラ逆数を求める必要があるのはどういうときか?

これは一言で言ってしまうと、有限体上での割り算を行うときです。私は数学の専門家じゃないので用語とか定義が間違っている可能性は高いですが、要はmod 11とか計算式の右側についている場合の割り算のときです。

詳しいことはまた別途書きたいと思いますが、楕円曲線上での加法や乗法を行う時には、まさに式の中に分数、つまり割り算が出てきてますし、式にはmodが付いています。

モジュラ逆数を具体例で理解する

最初に言っておきますとQiitaに楕円曲線暗号の超簡単な理論の紹介という素晴らしい記事がありまして、以下の内容はそちらをかなり参考にさせて頂いております。是非リンク先の記事も読んで下さい。

ではまず割り算の前に足し算、引き算、掛け算については以下のように普通に出来ることを確認したいと思います。

ちなみに≡は合同を意味します。

3+9 \equiv 1 \mod11

4-3 \equiv 1 \mod11

3 \times 4 \equiv 1 \mod 11

はい、そうですね。普通に四則演算した結果の答えを11で割って、その余りを本当の答えとすればいいだけです。

次は割り算です。例えば

3 \div 4 \mod11

は一体どう計算したらよいでしょうか?\frac{3}{4}、つまり0.75を11で割った余りが答えでしょうか?

割り算を「逆数を掛け算する」と捉える

ここでまず、そもそも\frac{1}{4}とは何かを考えてみたいと思います。\frac{1}{4}とは4と掛け算すると答えが1になる値という定義することができると思います。数学用語で\frac{1}{4}は4の逆数と言います。

\frac{1}{4}が4の逆数であることは例えmod 11のもとに計算が行われても変わりはありません。

つまり

3 \div 4 = 3 \times \frac{1}{4} = 3 \times (4の逆数) \mod11

を求めればいいことになります。この4の逆数をmod 11のもとに求めればいいわけで、これがモジュラ逆数と呼ばれるものです。「mod 11のときの4の逆数」を求めればいいわけです。

mod 11のときの4の逆数は何なのかを考えてみる

コードを書いて「mod 11のときの4の逆数」を求める前に、普通に人間の感覚を使って求めたいと思います。

上述しましたが、\frac{1}{4}とは4と掛け算すると答えが1になる値という定義することができます。mod 11のとき、4に何を掛け算すると答えが1になるでしょうか?そうです。4 \times 3 \equiv 1 \mod11なので、3をかけると1になります。ちなみに14や25でも掛け算すると1になります。

つまり3 \div 4 \mod113 \times 3 \mod11と同じなのです。これで晴れて計算を行うことが出来ます。この記事のタイトルにあるように3 \div 4 \mod11の答えは9となります。

このように\mod 11などのもとに割り算を行う場合、分母にある値の逆数(モジュラ逆数)を求めてから、掛け算の形に直して計算を行う必要があります。これがモジュラ逆数を計算する必要がある理由です

ちなみに割り算が割り切れるときは逆数を求めなくても計算できてしまいます。もちろん逆数を求めても計算できます。

 6 \div 3 \equiv 2 \mod11
 6 \times \frac{1}{3} = 6 \times (3の逆数) = 6 \times 4 \equiv 2 \mod11

となって答えが同じであることが確認できたと思います。

モジュラ逆数をプログラムで求める

先ほどのWikipediaページに計算方法が紹介されています。

モジュラ逆数 – Wikipedia

ある整数 a \mod mにおける逆数を求めたいとき、その逆数を xとすると以下が成り立ちます。

 ax \equiv 1 \mod m

これは言い換えると、 axから1を引くと mもしくは mを整数倍した数になるということです。その整数倍を qmと表現すると以下が成り立ちます。modじゃなくて普通の計算式になっていることにご注意ください。

 ax - 1 = qm

整理するとこうなります。

 ax - qm = 1

我々は xを求めたいわけですが、ここで拡張ユークリッドの互除法というアルゴリズムを使って求めることが出来ます

ユークリッドの互除法という最大公約数(GCD)を求めるアルゴリズムをご存知かと思いますが、その拡張版として、

 ax + by = \gcd(a, b)

を成り立たせる整数ペア x,  yもついでに求めることができるというアルゴリズムです。上の ax - qm = 1ではGCDは既に1だと分かっていますけれど、

 ax + (-mq) = \gcd(a, m)

とすれば同じアルゴリズムで x qが求まりそうです。こちらのWikipediaページに乗っているコードを参考にしてC#で書いてみたいと思います。

using System;
namespace ModInverseTest
{
class Program
{
public static void ExtendedEuclid(int a, int b)
{
var x = 0;
var y = 1;
var gcd = b;
var prev_x = 1;
var prev_y = 0;
var prev_gcd = a;
var temp = 0;
while (gcd > 0)
{
int quotient = prev_gcd / gcd;
temp = x;
x = prev_x - quotient * temp;
prev_x = temp;
temp = y;
y = prev_y - quotient * temp;
prev_y = temp;
temp = gcd;
gcd = prev_gcd - quotient * temp;
prev_gcd = temp;
}
Console.WriteLine(string.Format("{0}*{1} + {2}*{3} = gcd({0},{2}) = {4}",
a,
prev_x,
b,
prev_y,
prev_gcd));
}
static void Main(string[] args)
{
ExtendedEuclid(1071, 1029);
}
}
}

以下が出力結果です。

1071*-24 + 1029*25 = gcd(1071,1029) = 21

では最後にこの関数を改造して、モジュラ逆数を求めるプログラムを書いてみます。冒頭で書いた通り私はこの関数を楕円曲線上の演算にて使う予定なので、大きな数にも対応できるようにBigInteger型を使います(ちなみにJavaのBigInteger型を使うと、ModInverse()というメソッドが既に実装されているようです。)

using System;
using System.Numerics;
namespace ModInverseTest
{
class Program
{
public static BigInteger ModInverse(BigInteger a, BigInteger m)
{
if (a < 0)
{
a = BigInteger.ModPow(a, 1, m) + m;
}
BigInteger x = 0, y = 1, gcd = m;
BigInteger px = 1, py = 0, pgcd = a;
BigInteger temp = 0;
while (gcd > 0)
{
BigInteger quotient = pgcd / gcd;
temp = x;
x = px - quotient * temp;
px = temp;
temp = y;
y = py - quotient * temp;
py = temp;
temp = gcd;
gcd = pgcd - quotient * temp;
pgcd = temp;
}
return px;
}
static void Main(string[] args)
{
Console.WriteLine("a = 3, m = 11, mod_inverse = " + ModInverse(3, 11));
Console.WriteLine("a = 4, m = 11, mod_inverse = " + ModInverse(4, 11));
}
}
}

以下が出力結果です。

a = 3, m = 11, mod_inverse = 4
a = 4, m = 11, mod_inverse = 3

正しくモジュラ逆数が求められています。

最後になりますが、モジュラ逆数のWikipediaにもあったように、もし mが素数であれば以下が成り立つようです。

 (整数aのモジュラ逆数) = a^{m-2} \mod m

.NETのBigIntegerにもModPow()という関数が用意されているので、mが素数のときであれば以下のように関数を書くこともできます。

public static BigInteger ModInverse(BigInteger a, BigInteger m)
{
return BigInteger.ModPow(a, m - 2, m);
}

まとめ

もともとはビットコインアドレスをスクラッチから自分で作ってみようと思っていたのですが、モジュラ逆数のことで躓いてしまい、色々と調べたり試したりすることになりました。

モジュラ逆数を求められないと、modが付いているとき(有限体上で)の式で割り算が出来ません。そしてこの割り算ができないと、楕円曲線上での加法や乗法ができません。そして楕円曲線上での加法や乗法ができないと、秘密鍵から公開鍵を作成できません。結果ビットコインアドレスを作ることができません。

以上です。次はビットコインアドレスの作り方を記事にします。

参照

追記(2017年10月26日)

負の数の逆数を入力とすると、逆数が0となってしまう問題がありました。修正の為、入力が負の数だった場合、同等の正の数に変換するようにしました。

ggplot2を使ってガントチャートを描く

ggplot2を使ってガントチャートを描いてみます。ガントチャートは、プロジェクトを進行する際の複数タスクの関連を図示するとき等によく使われるチャートです。

まずこのようなデータフレームを用意します。

Task Date Type
A 2020-01-01 Start
A 2020-01-05 End
B 2020-01-06 Start
B 2020-01-15 End
C 2020-01-04 Start
C 2020-01-12 End
A 2020-01-13 Start
A 2020-01-20 End
B 2020-01-18 Start
B 2020-01-22 End

ご覧の通り、それぞれのタスクごとに始まりの日付と終わりの日付をそれぞれひとつのレコードとして保持します。終わったタスクが再度始まるケースも想定しています、というかそれがなければ話はずっと単純です。

次に以下の処理で各タスクのStart-Endペアごとに番号を振っていきます。

library(dplyr)
df %<>%
arrange(Task, Date) %>%
group_by(Task, Type) %>%
mutate(TaskSeq = row_number())

これでデータフレームの中身はこのようになります。

Task Date Type TaskSeq
A 2020-01-01 Start 1
A 2020-01-05 End 1
A 2020-01-13 Start 2
A 2020-01-20 End 2
B 2020-01-06 Start 1
B 2020-01-15 End 1
B 2020-01-18 Start 2
B 2020-01-22 End 2
C 2020-01-04 Start 1
C 2020-01-12 End 1

ループを使い、このデータフレームをTaskSeqごとにgeom_line()を使って図示していきます。

library(ggplot2)
gp <- ggplot()
for(seq in 1:max(df$TaskSeq)){
gp <- gp +
geom_line(data = subset(df, TaskSeq == seq),
aes(x = Date,
y = Task,
color = Task),
size = 5)
}
print(gp)

ガントチャートが図示できました。

f:id:rintaromasuda:20200323215755j:plain

オフェンスリバウンドを取った直後に何が起こっているのか?

オフェンスリバウンドを取ると、解説者の中川聴乃さんが嬉しそうに「ナイスリバンッ!!」と言ってくれます。あれ、最高です言われてみたいです。

という冗談はさておき、前回の記事に引き続いてオフェンスリバウンドの話題です。Bリーグの今季ここまでの全オフェンスリバウンド7,634本について、その直後にどんなプレーが生まれているのかを調べてみました。

「リバウンドを制する者はゲームを制する」、よく知られた言葉ですが、現代では無理にオフェンスリバウンドに行くのではなく、ディフェンスの戻りを優先した方が良い効果が出やすいと聞いたこともあります。

下がその7,634本の直後にどうなったのかのデータです。BリーグのPlay by Playにはポゼッションの変わらないアウトオブバウンズの記録は残っていないので、「直後」と言っても一回ボールがコートの外に出てプレーが止まっている場合もあります。

ついでに期待値も求めてみたのですが、フリースローで得られる点数は、バスケットカウントの場合などは無視して、適当に確率75%の2回分と設定してあります。また、当然再びオフェンスリバウンドを取得する可能性もある訳ですが、単純化の為にそれも無視しています。

直後のプレー 回数 起きている割合 得点 期待値
2点 成功 ペイント内 2262 29.6% 2 0.592611999
2点 失敗 ペイント内 1537 20.1% 0 0
3点 失敗 903 11.8% 0 0
ファウル取得(FTなし) 674 8.8% 0 0
ファウル取得(FTあり) 577 7.6% 1.5 0.113374378
ターンオーバー 541 7.1% 0 0
3点 成功 472 6.2% 3 0.185485984
2点 失敗 ペイント外 395 5.2% 0 0
2点 成功 ペイント外 204 2.7% 0 0
その他 40 0.5% 0 0
オフェンスファウル 29 0.4% 0 0
7634 0.89147236

オフェンスリバウンドの直後、約50%がペイント内で再びシュートを放ちにいっています。ファウルの取得もペイント内のシュートに対してだと仮定するならば、実に約58%はペイント内でのシュートに行っています。その成功率も約60%と高めです(2262 / (2262 + 1537))

次に多いのはスリーポイントで、オフェンスリバウンド後のプレーの約18%はスリーを打ちにいったようです。ちなみに「オフェンスリバウンド直後のスリーは確率がいい」と述べられいるのを聞いたことがあるのですが、ここでは約34.3%という成功率になりました。

ちなみにペイント外の2点エリアの場合、そのシュート成功率は約34.1%とかなり低めです。

ターンオーバーとオフェンスファウルで7.5%にもなっているのは面白い、というかもったいないですね。オフェンスリバウンドの13~14本に1本くらいは無駄にしてしまったということになります。

オフェンスリバウンド1回で約0.89点の期待値。この値を高いと見るか、低いと見るかは、相手のトランジションオフェンスのスキルや自チームのトランジションディフェンスの習熟度にもよります。

トランジションは最も容易に点が取れるシチュエーションのひとつであることを考えると、0.89点はチャレンジするには少し物足りないかもしれません。

以下、関連したツイートです。

ペリーメーターからのシュートはオフェンスリバウンドも取りづらい

以前NBAの方でこんなデータが紹介されていました。要はショットロケーションによってどうオフェンスリバウンドの確率が変わってくるかということですね。

端的に言えば制限区域内(ゴール下)やペイント内のシュートだとオフェンスリバウンドが最も取りやすい事と、スリーポイントよりもミッドレンジの方がオフェンスリバウンドの確率が悪いというのがポイントだと思います。

Play by Playのデータを解析し、今シーズンのB1についても調べてみました。具体的に言うと、ここまでの全シュートミス23,327本について、ディフェンスリバウンドに終わったのか、オフェンスリバウンドに終わったのかを調べました。

以下が結果です。ちなみにフリースローは当然ながら最後の一本のみ対象にしています。

シュートの種類 シュートミス総数 OREB DREB OREB%
スリー 9054 2499 6555 27.6%
2P(ペイント内) 8383 2072 5663 32.4%
2P(ペイント外) 4381 1042 3339 23.8%
2P 計 9002 3762 12764 29.5%
FG 計 15557 6261 21818 28.7%
フリースロー 1509 180 1329 11.9%

Bリーグの方でもどうやら2点のエリアのペイント外、いわゆるペリメーターと呼ばれるエリアにおけるシュートではオフェンスリバウンドの確率が低そうです。

近年このエリアは得点の期待値が低いことで有名。簡単に言えば2点のエリアのくせにあまり確率良く決められないことで嫌われがちです。

そこに来てオフェンスリバウンドも取りづらいというデータもあれば、ますます避けられるプレーになるかもしれません。ファウルのもらいやすさも考慮すると更にでしょうか。

なぜペリメーターからのシュートだとスリーポイントよりもオフェンスリバウンドが取りづらいのでしょうか。流れの中で打ってないタフショットになりがちだからでしょうか。それともリバウンドを取るべきインサイド陣が打ちがちだからでしょうか。考えてみると面白いかもしれません。

それにしても、NBAもBリーグもフリースローのOREB%って10%以上もあるんですね!気を抜いてしまいがちな瞬間ですが、油断は禁物です。