LoginSignup
0

More than 3 years have passed since last update.

適当な文字列を 0 から 1 までの実数に振り分けるロジック (PHP/TypeScript)

Last updated at Posted at 2020-09-22

PHP-7.2 TypeScript-3.7

最近 A/B テストのように確率的な条件分岐を実現する文脈で、適当な入力文字列から区間 $ [0, 1] $ に含まれる数値を出力するようなハッシュ関数を実装する機会がありました。単に定められた範囲の数値を出力するだけでなく、出力される数値に偏りが出ないように(関数の出力値の分布が連続一様分布とみなせる 1 ように)するという要件もありました。

イメージとしては以下のコードのようになります。

// 同じ文字列を入力に取ると同じ数値が出力される
strToNumberBetweenZeroAndOne('abc');        // output: 0.7283949105904
strToNumberBetweenZeroAndOne('abc');        // output: 0.7283949105904

// 別の文字列を入力に取ると別の数値が出力される
strToNumberBetweenZeroAndOne('Hello');      // output: 0.095208030923864
strToNumberBetweenZeroAndOne('World!');     // output: 0.31755707966684
strToNumberBetweenZeroAndOne('0123456789'); // output: 0.51892998626938

// 色々な文字列に対して出力値をプロットしていくと、数値の確率分布が区間 [0, 1] の一様分布に近づく。

このような関数を PHP と TypeScript で実装したので紹介します。

実装

PHP

PHP の場合は以下のように実装できます。

function strToNumberBetweenZeroAndOne(string $message): float
{
    // 1
    $hashHex = hash('sha256', $message);

    // 2
    $maxHex = str_repeat("f", 64);
    return hexdec($hashHex) / hexdec($maxHex);
}

1 の部分では hash('sha256', $string) で入力文字列を SHA256 2 で64桁の固定長16進数に変換し、 hexdec() でその10進数表現を得ています。文字列から16進数への変換は bin2hex で行うこともできますが、文字列のサイズが大きすぎると桁あふれを起こしてしまうので使用を避けています。

2 の部分では 1 で得られた10進数を64桁の16進数の最大値 str_repeat("f", 64) の10進数表現で割ることで数値を算出しています。

TypeScript

import CryptoJS from 'crypto-js'

const strToNumberBetweenZeroAndOne = (message: string): number => {
  const hashHex = CryptoJS.SHA256(message).toString()

  const maxHex = 'f'.repeat(64)
  return parseInt(hashHex, 16) / parseInt(maxHex, 16)
}

内容は PHP と特に変わりませんが、 SHA256 のハッシュ化を行うために crypto-js というライブラリを利用しています。

検証

実際にランダムな文字列を入力にメソッドの出力値 3 を集計してヒストグラムを作成してみると、おおよそ一様分布になっていることが分かります。

メソッドの出力値を集計して作成したヒストグラム


  1. 統計学に関しては門外漢なので、数学的に厳密な連続一様分布がどう表されるべきかは特に考慮しておらず、 A/B テストのような用途で実用性を損なわない精度の出力が得られることを目指しています。また統計学の用語の使い方が誤っている等あれば編集リクエストをいただけると助かります! 

  2. 単に強度や処理速度などの要件に応じて他のハッシュ関数を用いても構いません。 

  3. ここではヒストグラムの作成用に出力値を100倍して小数点を切り捨てています。 

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
What you can do with signing up
0