LoginSignup
7
4

More than 3 years have passed since last update.

空気ガチャを回しましょう

Last updated at Posted at 2020-06-07

はじめに

くだらない中で学んだことが多かったので共有

アイデアの先人
空気の成分30連ガチャ

空気を構成している要素、どこまで言えますか?
窒素、酸素、二酸化炭素、アルゴン...あとは知りませんでした。

こちらのサイトでガチャを回せます。

空気ガチャ排出率

空気を構成している成分は空気 - Wikipediaによると

成分 化学式 体積比 割合(vol%)
窒素 N2 78.084
酸素 O2 20.9476
アルゴン Ar 0.934
二酸化炭素 CO2 0.0390
ネオン Ne 0.001818
ヘリウム He 0.000524
メタン CH4 0.000181
クリプトン Kr 0.000114
二酸化硫黄 SO2 0.0001
水素 H2 0.00005
一酸化二窒素 N2O 0.000032
キセノン Xe 0.0000087
オゾン O3 0.000007
二酸化窒素 NO2 0.000002
ヨウ素 I2 0.000001

空気を構成している成分の体積比をガチャの排出率とします。
※Wikipedia内の「表2: 乾燥空気の微量成分」についてはあまりにも微量なので今回は無視します。

ソースコード

air_gacha.py
"""
https://ja.wikipedia.org/wiki/%E7%A9%BA%E6%B0%97#%E6%88%90%E5%88%86
表1: 乾燥空気の主な組成(国際標準大気、1975年)
成分  化学式   体積比 割合(vol%)  ppm ppb 備考
窒素  N2  78.084  780,840 -   [12]
酸素  O2  20.9476 209,476 -   [12]
アルゴン    Ar  0.934   9,340   -   [12]
二酸化炭素 CO2 0.0390  390 -   +*2011年の値[13][12][注 2]
ネオン   Ne  0.001818    18.18   -   [12]
ヘリウム    He  0.000524    5.24    -   [12]
メタン   CH4 0.000181    1.81    1813±2 +2011年の値[13][12][注 3]
クリプトン Kr  0.000114    1.14    -   [12]
二酸化硫黄 SO2 0.0001> 1>  -   *[12]
水素  H2  0.00005 0.5 -   [12]
一酸化二窒素  N2O 0.000032    0.32    324.2±0.1  +*2011年の値[13][12][注 4]
キセノン    Xe  0.0000087   0.087   87  [12]
オゾン   O3  0.000007>   0.07>   70> *[注 5][12]
二酸化窒素 NO2 0.000002>   0.02>   20> *[12]
ヨウ素   I2  0.000001>   0.01>   10> *[12]
"""
import random
import time
from decimal import Decimal
dic = {"窒素 N2":78.084, "酸素 O2":20.9476, "アルゴン Ar":0.934, "二酸化炭素 CO2":0.0390, "ネオン Ne":0.001818, "ヘリウム He":0.000524, "メタン CH4":0.000181, "クリプトン Kr":0.000114, "二酸化硫黄 SO2":0.0001, "水素 H2":0.00005, "一酸化二窒素 N2O":0.000032, "キセノン Xe":0.0000087, "オゾン O3":0.000007, "二酸化窒素 NO2":0.000002, "ヨウ素 I2":0.000001}
com = ["窒素", "酸素", "アルゴン", "二酸化炭素", "ネオン", "ヘリウム", "メタン", "クリプトン", "二酸化硫黄", "水素", "一酸化二窒素", "キセノン", "オゾン", "二酸化窒素", "ヨウ素"]
che = ["N2", "O2", "Ar", "CO2", "Ne", "He", "CH4", "Kr", "SO2", "H2", "N2O", "Xe", "O3", "NO2", "I2"]
keys = []
for i in dic.keys():
    keys.append(i)
su = 0
for i in dic.keys():
    su += int(Decimal(str(dic[i]))*(10**7))
    dic[i] = int(Decimal(str(dic[i]))*(10**7))
out = [0 for i in range(len(keys))]
que = 10**6
for q in range(que): #10**6は10秒, 10**7は2分かかる
    t = time.perf_counter()
    random.seed(t)
    ra = random.randint(0, su)
    res = 0
    if 0 <= ra < dic[keys[0]]:
        out[0] += 1
    else:
        res += dic[keys[0]]
        for i in range(1, len(keys)):
            """
            if i >= 5:
                print("!!!!!!!!!!!!")
                print(out)
            """
            if res <= ra < res + dic[keys[i]]:
                out[i] += 1
                break
            res += dic[keys[i]]

#半角, 全角による文字の"揺らぎ"を整形するための関数 
import unicodedata
def left(digit, msg):
    for c in msg:
        if unicodedata.east_asian_width(c) in ('F', 'W', 'A'):
            digit -= 2
        else:
            digit -= 1
    return msg + ' '*digit

print("空気ガチャ" + "{:.0e}".format(que) + "(" + str(que) + ")" + "連")
for i in range(len(keys)):
    print(left(12, com[i]), left(3, che[i]), "{:>7}".format(out[i]))

実行結果

que = 10**6のとき

空気ガチャ1e+06(1000000)連
窒素         N2   780580
酸素         O2   209579
アルゴン     Ar     9406
二酸化炭素   CO2     391
ネオン       Ne       32
ヘリウム     He        5
メタン       CH4       3
クリプトン   Kr        1
二酸化硫黄   SO2       2
水素         H2        1
一酸化二窒素 N2O       0
キセノン     Xe        0
オゾン       O3        0
二酸化窒素   NO2       0
ヨウ素       I2        0

que = 10**7のとき

空気ガチャ1e+07(10000000)連
窒素         N2  7809540
酸素         O2  2092942
アルゴン     Ar    93396
二酸化炭素   CO2    3808
ネオン       Ne      214
ヘリウム     He       54
メタン       CH4      12
クリプトン   Kr       11
二酸化硫黄   SO2       6
水素         H2       10
一酸化二窒素 N2O       6
キセノン     Xe        1
オゾン       O3        0
二酸化窒素   NO2       0
ヨウ素       I2        0

注意ポイント

浮動小数点数の誤差

float型の扱いには注意しましょう。
コンピュータの内の2進数の世界では小数を扱うとき、正確な値を扱うことができません。

print(0.1 + 0.2)
#実行結果: 0.30000000000000004

どこから現れた0.00000000000000004!!!!!!!!
そこでPythonで小数点以下を扱うときはDecimal()を使うとよいでしょう。
15. 浮動小数点演算、その問題と制限 — Python 3.8.3 ドキュメント

from decimal import Decimal
print(Decimal("0.1") + Decimal("0.2"))
#実行結果: 0.3

コード内で

for i in dic.keys():
    su += int(Decimal(str(dic[i]))*(10**7))
    dic[i] = int(Decimal(str(dic[i]))*(10**7))

としている部分が誤差の回避に該当しています。
10**7を掛けて排出率を整数で扱おうとしているときに、計算が行われているので浮動小数点型だと誤差が発生してしまいますね。

ランダムの作成

time.time()は精度があまりよくない? - Qiita
for文が回るのが早すぎるのでtime.time()では同じ時刻を生成してしまい、シード値が同じになる可能性が高いです(Windows環境では実際そうだった)。
そこで精度の高いtime.perf_counter()を使います。

t = time.perf_counter()
random.seed(t)
ra = random.randint(0, su)

半角, 全角による文字の"揺らぎ"を整形

Pythonのunicodedataライブラリを使って、全角文字も半角文字も揃えて表示する - Qiita
見やすい出力を心がけましょう。

for i in range(len(keys)):
    print(left(12, com[i]), left(3, che[i]), "{:>7}".format(out[i]))

感想

確率は偏る。

7
4
2

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