HeQihan

HeQihan

到底収集したアンケートは何件ですか?

広く知られているように、アンケートの収集は面倒で報われない作業です。過去にはオフラインでのアンケート収集は非常に手間がかかりましたが、オンラインのアンケート収集も依然として非常に楽な作業ではありません。私自身の例を挙げると、最大の影響力を持っても 155 件のアンケートしか収集できず、そのアンケートは主に単一選択肢の質問でした。

Forms データ

当然、誰かが悪知恵を働かせることになります。結局のところ、最後は一つの割合として計算されるので、数字を比例的に拡大すれば、割合は変わらず、自分の作業量を大きく見せることができ、計算量もかなり減ります。まさに二難自解です。

しかし、グルダン、その代償は何でしょうか?

互質#

簡単な概念を導入しましょう —— 互質。

もし二つの非零整数の最大公約数が 1 であれば(つまり、それらの公因数は 1 だけ)、これらの二つの数は互質であると言います。互質には非常に興味深い性質があります。

  • 1 との関係:1 は任意の整数と互質です。なぜなら、1 の因数は自分自身だけだからです。
  • 素数の特性:異なる素数は必ず互質です。例えば、3 と 5。
  • 隣接数の法則:隣接する二つの自然数(例えば 8 と 9)や隣接する奇数(例えば 15 と 17)は必ず互質です。
  • 判断方法:最大公約数を計算することで(例えばユークリッドのアルゴリズム)、二つの数が互質かどうかを判断できます。

もし数字を比例的に拡大し、分子と分母を同時に拡大すれば、割合は変わらないものの、分子と分母は必然的に互質ではなくなります。逆に言えば、互質な数のペアを見つけて、その結果が計算された割合に近づければ、実際のアンケート数を大まかに推測できます。どんなスケールでも、必ず法に従います!

どう操作するか#

アンケート数をNN、割合をa1,a2,a3,ana_1,a_2,a_3,\cdots a_na1,a2,a3,ana_1,a_2,a_3,\cdots a_nに対応する分子をb1,b2,b3,bnb_1,b_2,b_3,\cdots b_nと仮定します。つまり、a1b1N,a2b2NanbnNa_1\approx\frac{b_1}{N},a_2\approx\frac{b_2}{N}\cdots a_n\approx\frac{b_n}{N}。一般的に、割合は小数点以下二桁を保持するので、私たちの精度は0.00010.0001未満でなければなりません。各b1b_1NNは整数なので、いくつかの数字を代入して計算できます。

もちろん、数字に対する感度を高めることもできます。簡単に言えば、1某数\frac{1}{某数}の値に注目することができます。1某数\frac{1}{某数}の分子と分母は必然的に互質であり、非常に目立つ特徴です。例えば、13\frac{1}{3}を見れば、アンケートはおそらく 3 件しかないと推測できます。自分が 1 件、友人が 1 件、異性が 1 件を記入したのかもしれません。性別比も考慮できます。

深く考えると、0.0222、0.1556、0.1778、0.4667 を見れば、これらの数は

1450.02222,誤差0.00002\frac{1}{45}≈0.02222,誤差 0.00002
7450.15556,誤差0.00004\frac{7}{45}​≈0.15556,誤差 0.00004
8450.17778,誤差0.00002\frac{8}{45}≈0.17778,誤差 0.00002
21450.46667,誤差0.00003\frac{21}{45}​≈0.46667,誤差 0.00003

この問題について言及すると、実に簡単な問題だと感慨せざるを得ません。少し目を凝らせば結果が見えてきます。とても簡単です。こんなに長く書いたので、記事もそろそろ終わりにすべきでしょう?

もちろん、それは不可能です。AI に感謝します。私の思考を整理し、さらに一歩進めてくれました。本当に素晴らしいです。私の脳を解放し、思考は非常にシンプルで、数字もそれほど大きくなく、暴力的に計算すれば良いのです。

import math

from itertools import product

from functools import reduce

  

def find_min_n_direct(decimal_list, tolerance=0.0001, max_n=10000):

    """

    直接検索法:小さい整数nから順に試し、条件を満たす最小値を見つける

    パラメータ:

        decimal_list: 小数のリスト

        tolerance: 誤差の許容度、nの値に対して

        max_n: 検索する最大n値

    戻り値:

        条件を満たす最小の整数n、見つからなければNoneを返す

    """

    for n in range(1, max_n + 1):

        valid = True

        for x in decimal_list:

            k = round(x * n)

            error = abs(x * n - k) / n

            if error > tolerance:

                valid = False

                break

        if valid:

            return n

    return None

  

# ユーザー入力

m = int(input("小数の個数 m を入力してください: "))

decimals = [float(input(f"{i+1} 番目の小数を入力してください: ")) for i in range(m)]

  

# 検索を実行

result = find_min_n_direct(decimals)

  

# 結果を出力

if result:

    print(f"条件を満たす最小の整数 n = \033[1;32m{result}\033[0m")

    # 詳細情報を印刷

    print("\n検証結果:")

    for x in decimals:

        k = round(x * result)

        error = abs(x * result - k)

        print(f"{x} × {result} = {x*result:.4f}{k} (誤差: {error:.4f})")

else:

    print("条件を満たす n が見つかりませんでした。")

ここで終わりにするべきでしょうか?もちろん、そうではありません。おそらく誰かが言うでしょう、もし私のアンケートデータが非常に正確で、分子と総アンケート数が互質でない場合、計算されたデータは総アンケート数よりも小さくなり、参考になりません。

もしそうなれば、それは非常に偶然なことです。別の概念を導入する必要があります。

オイラー関数#

オイラー関数(Euler's totient function)、記号は (φ(n)\varphi(n))、は数論において正整数nn以下でnnと互質な正整数の数を計算するための関数です

定義#

正整数nnに対して、φ(n)\varphi(n)11からnnの間でnnと互質な数の個数を表します。例えば:

  • φ(8)=4\varphi(8) = 4、なぜなら1,3,5,71, 3, 5, 788と互質です。

  • φ(12)=4\varphi(12) = 4、なぜなら1,5,7,111, 5, 7, 111212と互質です。

  • 特別な値φ(1)=1\varphi(1) = 1、なぜなら11は自分自身と互質です。

核心的性質#

  1. 積性関数 

   aabbが互質であれば、φ(ab)=φ(a)φ(b)\varphi(ab) = \varphi(a)\varphi(b)

  1. 素数のオイラー関数 

   ppが素数であれば、φ(p)=p1\varphi(p) = p-1、なぜならpp未満のすべての数はppと互質です。

  1. 素数の累乗のオイラー関数 

   ppが素数、kkが正整数であれば、φ(pk)=pkpk1\varphi(p^k) = p^k - p^{k-1}。 

   例えば、φ(9)=φ(32)=93=6\varphi(9) = \varphi(3^2) = 9 - 3 = 6

  1. 一般的な計算公式 

   nnの素因数分解がn=p1k1p2k2pmkmn = p_1^{k_1} p_2^{k_2} \cdots p_m^{k_m}であれば:

φ(n)=n(11p1)(11p2)(11pm)\varphi(n) = n \left(1 - \frac{1}{p_1}\right)\left(1 - \frac{1}{p_2}\right) \cdots \left(1 - \frac{1}{p_m}\right)

   例えば、n=12=2231n = 12 = 2^2 \cdot 3^1であれば:

φ(12)=12(112)(113)=121223=4\varphi(12) = 12 \left(1 - \frac{1}{2}\right)\left(1 - \frac{1}{3}\right) = 12 \cdot \frac{1}{2} \cdot \frac{2}{3} = 4

グラフ#

オイラー関数のグラフは大体このようになります。

Figure_1

もしnnの素因数分解がn=p1k1p2k2pmkmn = p_1^{k_1} p_2^{k_2} \cdots p_m^{k_m}であれば、φ(n)=n(11p1)(11p2)(11pm)\varphi(n) = n \left(1 - \frac{1}{p_1}\right)\left(1 - \frac{1}{p_2}\right) \cdots \left(1 - \frac{1}{p_m}\right)という公式を使って、以下のコードでこの関数を描くことができます。

import matplotlib.pyplot as plt

import numpy as np

  

def euler_phi(n):

    """オイラー関数φ(n)を計算:n以下でnと互質な正整数の個数"""

    result = n  # nで初期化

    # すべての可能な素因数をチェック

    p = 2

    while p * p <= n:

        if n % p == 0:

            # pはnの素因数の一つ

            while n % p == 0:

                n //= p

            result -= result // p  # φ(n) = φ(n) * (1 - 1/p)

        p += 1

    # 最後にnが1より大きければ、nは素数です

    if n > 1:

        result -= result // n

    return result

  

# 1から1000までのオイラー関数値を計算

n_values = np.arange(1, 1001)

phi_values = [euler_phi(n) for n in n_values]

  

# グラフを作成

plt.figure(figsize=(12, 8))

plt.scatter(n_values, phi_values, s=5, alpha=0.6)  # 点のサイズを小さくして可読性を向上

plt.plot(n_values, phi_values, 'r-', alpha=0.2, linewidth=0.5)  # 線を薄くする

  

# グラフのタイトルとラベルを追加

plt.title(' φ(n) (1-1000)')

plt.xlabel('n')

plt.ylabel('φ(n)')

plt.grid(True, alpha=0.3)

  
  

plt.legend(['φ(n)'], loc='upper right')

  

# グラフを表示

plt.tight_layout()

plt.show()

グラフからは、φ(n)\varphi(n)の値は整数であり、グラフは離散点で構成されていますが、通常は折れ線または散布図の形式で連続的な傾向を示します。

nnが素数のとき、φ(p)=p1\varphi(p) = p-1であり、グラフ上では直線y=x1y = x-1に沿って分布します。例えば、n=11n=11のとき、φ(11)=10\varphi(11)=10です。

nnが累乗合成数のとき、例えばn=9=32n=9=3^2φ(9)=6\varphi(9)=6であり、値は高いですが同じ範囲の素数よりも低いです。

nnが多くの素因数を持つ合成数のとき、例えばn=30=235n=30=2 \cdot 3 \cdot 5φ(30)=8\varphi(30)=8であり、隣接する素数点よりもはるかに低いです。

φ(n)n\frac{\varphi(n)}{n}の特徴#

もちろん、さらに進んで、アンケートデータが非互質な分子と分母に当たる可能性を見てみましょう。やはり古い性質ですが、もし $n$ の素因数分解がn=p1k1p2k2pmkmn = p_1^{k_1} p_2^{k_2} \cdots p_m^{k_m}であれば、φ(n)=n(11p1)(11p2)(11pm)\varphi(n) = n \left(1 - \frac{1}{p_1}\right)\left(1 - \frac{1}{p_2}\right) \cdots \left(1 - \frac{1}{p_m}\right)となります。

したがって、φ(n)n=(11p1)(11p2)(11pm)\frac{\varphi(n)}{n} = \left(1 - \frac{1}{p_1}\right)\left(1 - \frac{1}{p_2}\right) \cdots \left(1 - \frac{1}{p_m}\right)となります。

これは非常に簡単です。アンケート数 $n$ の素因数がpmp_mであると仮定すると、pm×np_m \times nnnが互質な分子と分母にランダムに衝突する確率は同じです。もし誰かのアンケートデータが非常に正確で、分子と総アンケート数(pm×np_m \times n)が互質でない場合、より大きなデータ範囲内でも同様に、分子と総アンケート数(nn)が互質でない確率も同じです。確率が同じであれば、なぜこの事例だけを報告するのか。したがって、前述の状況は明らかに可能性が非常に低いのです。ここでは刑法ではないので、何かを謙虚に保つ必要はありません。このような非常に小さな確率の状況は完全に無視できます。

もしplp_lがアンケート数nnの素因数でない場合、pl×np_l \times nnnが互質な分子と分母にランダムに衝突する確率は大きく異なります。nnの素因数を見て、nnの素因数でないいくつかを見つけて、nnと掛け合わせてみて、適切なものを選べば良いのです。

しかし、皆さんが落胆する必要はありません。グラフを見てみましょう。

以下のグラフがあります。

Figure_2

範囲を少し広げることもできます。

Figure_3

こう見ると、ずっと明確になります。

グラフは明らかなフラクタル特性と自己相似パターンを示しており、周期的に下降する構造が複数観察できます。各「支線」は特定の素数の倍数に対応し、nnが増加するにつれて、全体の傾向は下降しつつ波動し、nnが増加するにつれて、φ(n)n\frac{\varphi(n)}{n}の平均値は6/π20.60796/\pi² ≈ 0.6079に近づきます。この結果は素数分布の漸近密度に関連しています。(以上は AI 生成)

したがって、衝突確率が明らかに減少しない限り、全体としてこの方法は比較的信頼できるものであり、誠意のない偽造を簡単に発見できます。

欠陥#

もちろん、この方法にも欠陥があります。前述の不正確な点に加え、通常のアンケート調査結果は小数点以下二桁のパーセンテージを保持するため、このコードの誤差の許容度は 0.0001 です。もしアンケートの数が 10000 件を超えると、得られる結果も 10000 件のみとなり、能力が制限されます。この場合、結果が小数点以下一桁のパーセンテージであれば、許容度は 0.001 となり、上限は 1000 件となります。

また、全探索の優雅さが不足しています。本来、AI に連分数のアルゴリズムを作成させたのですが、テストに合格せず、いくつかのケースで計算された数字が大きくなりました。結局、10000 個の数字があるので、Python を使っているのですから、効率を追求せずに全探索すれば良いのです。

最後にもう一つの欠陥がありますが、それは私の発見の難易度を上げることになるので、言いません!

最後に、いくつかの知網の文献を使って練習し、皆さんにその正確性を見せてみましょう。

image

image

image

image

image

image

OK、作業終了です。

この記事は Mix Space によって xLog に同期更新されました。元のリンクは https://www.actorr.cn/posts/default/Eulerstotient

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。