宝くじの当選率に関する真実: JavaScript を使用して宝くじの背後にあるランダム アルゴリズムを見破る

もともとこの記事は「もし私が宝くじシステム開発者だったら」というつもりでしたが、よく考えたら記事内でJavaScriptを引用しすぎると純度が低くなってしまいます。この記事で述べたように、誤解を招くものであれば、ローソクの価値はありません。

したがって、これは単に「宝くじの当選率の真実: JavaScript を使用して宝くじの背後にあるランダム アルゴリズムを見抜く」と呼ばれていますが、これはもう少し明確です。実際の宝くじシステムはこのように開発されていないことを宣言しておきます。たとえそれがランダム性に基づいていないとしても、宝くじの公平性は信頼されるべきです。

ゴシップ

最近、宝くじにハマっていて、お金持ちになれたら、家族を「昇天」させることもできるのではないかと想像しています。

私も宝くじを買うときは、1,700万枚の賭けの中でどんな数字が目立つのか、乱数を試してみたり、厳選してみたり、規則性のあるパターンを探してみたり、クローラーも使って考えたりしました。統計はばかばかしいものです。

私たちのデフォルトの宝くじシステムは、一等賞の抽選を達成するための統計に基づいています。したがって、当然のことながら、歴史上一等賞は、現在の期間で統計的比率が最も低い賭け金であるはずです。そこで、最初は次のように考えました。

  1. 履歴内のすべての当選番号を取得する

  2. コードを使用して、すべての数字の当選回数をカウントします。

  3. 最も可能性の低い番号で並べ替える

  4. 順番に新しい数字をいくつか形成します

お金を稼ぎたいという欲求のはけ口です。気まぐれと言っても過言ではありません。話題が豊富です(笑)。

私は上記のアイデアをすでに実践し、1年近く使用していますが、役に立ちません。使ってはいけません!もちろん、挑戦することもできます。勝てば、おめでとうございます。あなたは選ばれた人です。

宝くじのルール

ここでの抽選ルールは「2色ボール」のルールを例に統一しており、その購入ルールは以下の通りです。

  1. 赤いボールは 6 個、オプションは 1 ~ 33 から選択され、繰り返し不可

  2. 青いボールは 1 つで、選択肢は 1 ~ 16 から選択されます。

  3. 合計 7 つの赤と青の 2 色のボールが賭け金となります

通常、一等賞は購入されたすべての賭けの中から 1 つの賭けを選択します。この賭けは複数の人が購入することも、1 人が購入した賭けの倍数になる場合もあります。

大まかに言うと、宝くじに当たる確率を計算する式は次のとおりです。

計算には組み合わせ数式を使用します。n要素から要素を取り出すためのk組み合わせ数式は次のとおりです。

C(kn)=n!k!(n−k)!C\binomial{k}{n}=\frac{n!}{k!(nk)!}C(nk)=k!(n-k )!ん!

式によれば、簡単なアルゴリズムを簡単に書くことができます。

function factorial(n) {
  if (n === 0 || n === 1) {
    return 1
  } else {
    return n * factorial(n - 1)
  }
}

function combination(n, k) {
  return factorial(n) / (factorial(k) * factorial(n - k))
}

console.log(combination(33, 6) * combination(16, 1)) // 17721088
复制代码

したがって、二色ボールのジャックポットに勝つ確率は、117721088\frac{1}{17721088}177210881であると結論付けることができます。

データ量

上記のアルゴリズムにより、宝くじの合計ベット数は であることがわかります 17721088。では、これほど多くのベット数で構成されるデータはどれくらいの大きさになるでしょうか?

単純な計算では、宝くじの紙幣は 14 個の数字で表すことができます。たとえば 01020304050607、オペレーティング システムでは、この数字の文字列のサイズは です 14B。すべての宝くじの紙幣が 1 つのファイルにある場合、このサイズになることがおおよそわかっています。ファイルの内容は次のとおりです。

const totalSize = 17721088 * 14 / 1024 / 1024 // 236.60205078125MB
复制代码

ひどい量です、もっと少なくすることは可能でしょうか?圧縮アルゴリズムを勉強してみよう!

0101 この数値はメモリ内で 2 バイト (2B) を占めますが、これを小文字に 置き換えると a 、その容量は 1B になり、全体の容量は約半分に削減できます。

このようにして、上記の特別なベット数は !01020304050607 と表すことができます 。abcdefg 

これは圧縮アルゴリズムの最も基本的な原理です。圧縮アルゴリズムにはさまざまな種類があり、大きく非可逆圧縮と可逆圧縮に分けられます。データ クラスの内容としては、一般的に可逆圧縮を選択します。

  • 非可逆圧縮アルゴリズム: これらのアルゴリズムは、データの圧縮時に一部の情報を破棄する可能性がありますが、通常は実際の使用に影響を与えることなく、より高い圧縮率を実現します。最も一般的なのは、画像、音声、およびビデオの圧縮アルゴリズムです。

  • 可逆圧縮アルゴリズム: これらのアルゴリズムは情報を一切破棄せず、入力データ内で繰り返されるパターンを検索し、それらを表すために短いシンボルを使用することで圧縮を実現します。可逆圧縮アルゴリズムは、テキスト、コード、構成ファイル、その他の種類のデータによく使用されます。

まず、いくつかのテスト データを準備します。次の単純な組み合わせ番号生成アルゴリズムを使用して、1000 個の組み合わせ番号を取得します。

function generateCombinations(arr, len, maxCount) {
  let result = []
  
  function generate(current, start) {
    // 如果已经生成的组合数量达到了最大数量,则停止生成
    if (result.length === maxCount) {
      return
    }

    // 如果当前已经生成的组合长度等于指定长度,则表示已经生成了一种组合
    if (current.length === len) {
      result.push(current)
      return
    }

    for (let i = start; i < arr.length; i++) {
      current.push(arr[i])
      generate([...current], i + 1)
      current.pop()
    }
  }

  generate([], 0)
  return result
}
复制代码

次に、2 色のボールを 1000 個生成する必要があります。赤いボールは 1 ~ 33 から選択され、青いボールは 1 ~ 16 から選択されます。

function getDoubleColorBall(count) {
  // 红球数组:['01', '02' .... '33']
  const arrRed = Array.from({ length: 33 }, (_, index) => (index + 1).toString().padStart(2, '0'))
  const arrRedResult = generateCombinations(arrRed, 6, count)

  const result = []
  let blue = 1
  arrRedResult.forEach(line => {
    result.push(line.join('') + (blue++).toString().padStart(2, '0'))
    if (blue > 16) {
      blue = 1
    }
  })

  return result
}
复制代码

取得した宝くじのコンテンツを次のステップのためにファイルに保存します。

const firstPrize = getDoubleColorBall(1000).join('')
fs.writeFileSync('./hello.txt', firstPrize)
复制代码

このようにして、ファイルの最初のバージョン、つまりファイル サイズを取得します。

予備圧縮アルゴリズムを試してみましょう。設定したばかりのルール、つまり数値を文字に置き換えるルールを、次のように JavaScript で実装します。

function compressHello() {
  const letters = 'abcdefghijklmnopqrstuvwxyzABCDEFG'
  const doubleColorBallStr = getDoubleColorBall(1000).join('')
  let resultStr = ''
  for (let i = 0; i < doubleColorBallStr.length; i+=2) {
    const number = doubleColorBallStr[i] + doubleColorBallStr[i+1]
    resultStr += letters[parseInt(number) - 1]
  }
  return resultStr
}

const firstPrize = compressHello()
fs.writeFileSync('./hello-1.txt', firstPrize)
复制代码

このようにして、サイズが次のような新しい hello ファイルが得られます。これは、私たちのアイデアを裏付けるものです。

このアルゴリズムに従えば、前のファイルを半分のサイズ、つまり に圧縮できますがこれが限界でしょうか? いいえ、上で述べたように、これは最も基本的な圧縮です。次に、より巧妙な方法を試してみましょう。 118.301025390625MB

もっと微妙な方法

圧縮アルゴリズムの原理についてはここではあまり説明しませんので、興味があれば同様の記事を探してご自身で読んでください。インターネット上の記事は品質にばらつきがあるので、お勧めしません。

ここで理解する必要があるのは、私たちが研究しているのは宝くじシステムであるため、そのデータ圧縮には次の特性がある必要があるということです。

  • データの損失がない、つまり可逆圧縮という特徴があります。

  • 上で示した例のように、転送されるファイルが非常に大きくなる可能性があるため、圧縮率はできるだけ小さくする必要があります。

  • 情報の送信を促進します。つまり、HTTP リクエストをサポートします。

content-encoding: gzipフロントエンドで作業することが多い学生は、プロジェクトの最適化の観点から、  HTTP リクエスト ヘッダーの共通パラメータによって gzip 配布用のリソース ファイルの変換も選択されることを知っておく必要があります。 日常的な使用では、などの WebpackライブラリRollupnginxに依存したり、 ネットワーク サーバーを介してリソースを完全に圧縮したりすることもよくあります。 gzip これにより、送信されるコンテンツが大幅に削減されるだけでなく、クライアントが損失なくソース ファイルを解凍してアクセスできるようになります。

gzip それで、圧縮を完了するために使用できますか ? 答えは「はい」です。 ツール ライブラリと対応する圧縮関数Node.js が提供されます 。zlib

const zlib = require('zlib')

const firstPrize = compressHello()
fs.writeFileSync('./hello-2.txt.gz', zlib.gzipSync(firstPrize))
复制代码

結果は次のとおりです。

14KB→3KBの圧縮処理を行いました!面白くないですか?しかし、もう一度言いますが、もっと小さくすることは可能でしょうか?もちろん!

content-encoding 応答ヘッダーは通常、返されるリソース応答エンコード形式に関するサーバーの設定情報であり、一般的な値は次の 3 つです。

  • gzip すべてのブラウザでサポートされるユニバーサル圧縮形式

  • brotligzip 古いブラウザではサポートされていない、圧縮パフォーマンスが向上し、圧縮率が小さい 新しい 圧縮形式

  • deflate 何らかの理由で広く使用されておらず、このアルゴリズムに基づいた圧縮形式もあります zlib が、あまり使用されていません

ブラウザが対応している圧縮形式はこの限りではありませんが、よく使われる圧縮形式を列挙しましたので、以下の3つの圧縮形式を使ってみましょう。

const firstPrize = compressHello()
fs.writeFileSync('./hello-2.txt.gz', zlib.gzipSync(firstPrize))
fs.writeFileSync('./hello-2.txt.def', zlib.deflateSync(firstPrize))
fs.writeFileSync('./hello-2.txt.br', zlib.brotliCompressSync(firstPrize))
复制代码

deflate と の 圧縮率は同等であることがわかりgzip 、驚くべきことに、brotliと の圧縮率は驚異的な 1KB に達しています! これは私たちが望んでいることではありませんか?

もっと小さくてもいいでしょうか?あははは、もちろん、HTTP サポートを考慮しない場合は、 7-zip 圧縮率の低い圧縮アルゴリズムを使用して圧縮を完了し、クライアントを使用して手動で解凍することもできます。しかし、これまでのところ、私たちはさらに重要な仕事を行っていません。

その前に、まず解凍プロセスを理解する必要があります。解凍後にデータが失われると、損失が利益を上回ります。

// 执行解压操作
const brFile = fs.readFileSync('./hello-2.txt.br')
const gzipFile = fs.readFileSync('./hello-2.txt.gz')
const deflateFile = fs.readFileSync('./hello-2.txt.def')

const brFileStr = zlib.brotliDecompressSync(brFile).toString()
const gzipFileStr = zlib.gunzipSync(gzipFile).toString()
const deflateFileStr = zlib.inflateSync(deflateFile).toString()

console.log(brFileStr)
console.log(gzipFileStr)
console.log(deflateFileStr)

console.log(brFileStr === gzipFileStr, brFileStr === deflateFileStr) // true, true
复制代码

上記のように、圧縮アルゴリズムの効果は驚くべきものですが、解凍されたデータは依然としてロスレスであることがわかります。

完全なデータ

完全な 17721088 ノート データを構築して、完全な圧縮アルゴリズムの能力をテストしてみませんか? brotli ここでは、と の アルゴリズムを使用して gzip 圧縮テストを個別に実行します。

まず、データを生成する関数を次のように変更する必要があります。

function generateAll() {
  const arrRed = Array.from({ length: 33 }, (_, index) => (index + 1).toString().padStart(2, '0'))
  const arrRedResult = generateCombinations(arrRed, 6, Number.MAX_VALUE)

  const result = []
  arrRedResult.forEach(line => {
    for (let i = 1; i <= 16; i++) {
      result.push(line.join('') + i.toString().padStart(2, '0'))
    }
  })

  return result
}

console.log(generateAll().length) // 17721088
复制代码

次に、初期圧縮を実行してファイルに書き込みます。

function compressAll() {
  const letters = 'abcdefghijklmnopqrstuvwxyzABCDEFG'
  const allStr = generateAll().join('')
  let resultStr = ''
  for (let i = 0; i < allStr.length; i += 2) {
    const number = allStr[i] + allStr[i+1]
    resultStr += letters[parseInt(number) - 1]
  }
  return resultStr
}

const firstPrize = compressAll()
fs.writeFileSync('./all-ball.txt', firstPrize)
复制代码

予想どおり、予備圧縮後のファイル サイズは約 118MB に達しましたが、実際の占有量は 124MB であり、コンピュータのストレージのカテゴリに属します。この記事では説明しません。興味のある学生は自分で確認してください。バイト単位で計算すると、そのサイズは次のようになります。

const totalSize = 124047616 / 1024 / 1024 // 118.30102539 MB
复制代码

現時点では期待通りの結果となっていますが、2つの圧縮アルゴリズムの実力を見てみましょう!

const firstPrize = compressAll()
fs.writeFileSync('./all-ball.txt.gz', zlib.gzipSync(firstPrize))
fs.writeFileSync('./all-ball.txt.br', zlib.brotliCompressSync(firstPrize))
复制代码

実はこれは衝撃的で、 brotli 期待値は高かったものの、まさか4Mというサイズにまで圧縮できるとは思っていませんでしたが、これは我々にとってはありがたいことであり、今後の展開に大きなメリットをもたらしてくれます。配布オペレーション!

2 つのランダムな賭け

宝くじ売り場で宝くじを購入する場合、ランダムな二重賭けは非常に一般的ですが、ランダムな数字を試すとどうなるでしょうか?

まず、宝くじデータの配信についてですが、まず、宝くじデータの配信におけるセキュリティと安定性の設計については、確かに疑問の余地はありませんが、現時点で検討すべき問題ではありません。それは、より低い程度の制御コストを達成できれば良いということです。

このシステムを設計したのがあなただと仮定して、乱数の勝率を制御するとしたら?私の答えは、既存の番号プールから選択することです。

各宝くじステーションが対応する番号プールを取得できる場合、答えは「データ配布!」です。データ分散方式を採用する場合、考慮すべき事項は次のとおりです。

  • いつ配布するか

  • データをソースに戻す方法

  • すべてのデータハイジャックを回避する方法

  • データを宝くじステーションに引き渡す戦略

2021年の公表情報によると、宝くじ売り場数は20万店(未検証、参考値なし)に達しましたが、現在の宝くじ売り場数は30万店と仮定します!

いつ配布するか

わかっていることは、宝くじの購入締め切りは午後8時、抽選時間は午後9時15分で、8時からの計画は次のとおりです。

  1. 現在の宝くじライブラリから、発生確率に従って数字を高いものから低いものへと並べます。

  2. 最初の50万ベットが選択され、30万の宝くじステーションに分配されますが、このときの宝くじステーションのデータは統一されています

  3. データは1時間ごとに同期される、他の宝くじ所の「特選データ」です

50万ノートのデータ量はどれくらいですか?やってみて:

function getFirstSend() {
  const letters = 'abcdefghijklmnopqrstuvwxyzABCDEFG'
  const doubleColorBallStr = getDoubleColorBall(500000).join('')
  let resultStr = ''
  for (let i = 0; i < doubleColorBallStr.length; i+=2) {
    const number = doubleColorBallStr[i] + doubleColorBallStr[i+1]
    resultStr += letters[parseInt(number) - 1]
  }
  return resultStr
}

const firstPrize = getFirstSend()
fs.writeFileSync('./first-send.txt.br', zlib.brotliCompressSync(firstPrize))
复制代码

わずか 1 枚の写真のサイズで、これらのデータを取得し、解凍して宝くじマシンに同期するのに 1 秒もかかりません。

解凍の例は次のとおりです。

function decodeData(brFile) {
  const result = []
  const content = zlib.brotliDecompressSync(brFile)
  // 按照七位每注的结构拆分
  for (let i = 0; i < content.length; i += 7) {
    result.push(content.slice(i, i + 8))
  }
  return result
}

const firstSend = fs.readFileSync('./first-send.txt.br')
const firstDataList = decodeData(firstSend)
console.log(firstDataList.length) // 500000
复制代码

取得した文字形式の宝くじを次のような数字に変換 する 方法 :abcdefga['01', '02', '03', '04', '05', '06, '01']

function letterToCode(letterStr) {
  const result = []
  const letters = 'abcdefghijklmnopqrstuvwxyzABCDEFG'
  for (let i = 0; i < letterStr.length; i++) {
    result.push((letters.indexOf(letterStr[i]) + 1).toString().padStart(2, '0'))
  }
  return result
}
复制代码

配布に関しては?比較のために、市場にあるいくつかの既存の概念を参照できます。以下は、Web サーバーの TPS の一般的な推定値、つまり、宝くじサーバーが 1 秒以内に処理できるリクエストの最大数です。

  • 低パフォーマンス: TPS が 50 未満で、個人のブログ、小規模ビジネスの Web サイトなど、トラフィックの少ないアプリケーション シナリオに適しています。

  • 中パフォーマンス: TPS は 50 ~ 500 で、中小規模の電子商取引 Web サイト、ソーシャル ネットワークなどの一般的な Web サイトおよびアプリケーション シナリオに適しています。

  • 高性能: TPS は 500 ~ 5000 で、大規模な電子商取引 Web サイト、ゲーム Web サイトなど、トラフィックの多い Web サイトやアプリケーション シナリオに適しています。

  • 超高性能: TPS は 5000 以上で、インターネット大手の Web サイトやオンライン ゲームなど、超高トラフィックの Web サイトやアプリケーション シナリオに適しています。

このモデルによると、50万台の宝くじステーションのデータ同期は100秒以内に完了します もちろん皆さん、これはスタンドアロンモードです 宝くじサービスをやりたいならスタンドアロンは絶対に無理です もしTPS を向上させたい場合は、サーバー クラスターを実行してください。サーバー クラスターが 100 個ある場合、これらのリクエストの処理にかかる時間はわずか 1 秒です。(ワガママですか? もちろんお金があればワガママでも構いません!) (これらのデータは理論に基づくもので参考値ではありません)

データをソースに戻す方法

とてもシンプルです!どのような種類のデータを取得する必要がありますか? ランダムアルゴリズムを使わずに宝くじデータを直接購入!それは、よく聞く「番号を守る」昔の宝くじプレイヤーのことです。

同様に、メディアの問い合わせによると(非参考)、宝くじ売り場の乗客数は 1 時間あたり 1 ~ 10 人で、営業時間は午前 9 時から午後 9 時までです。 1日あたり100名になります!

この場合、すべての宝くじ売り場の総乗客数は 100 * 500000 = 50000000 となり、これは約 5,000 万人時間に相当し、そのうちの約 50% が「ナンバーキーパー」に属します。宝くじ売り場の既知の番号を除外する必要があるかもしれません。しかし、ここでは最初にそれを扱うのではなく、最初にすべての見積もりを作成してから、

サーバーが保持する必要がある最大 TPS は次のとおりです。

// 服务器集群数量
const machineCount = 100
// 总访问量,50%中的号码才会上报
const totalVisit = 50000000 * 0.5 // 25000000
// 总的时间,因为我们计算的是 10个小时的时间,所以应该计算的总秒数为 36000 秒!
const totalSeconds = 10 * 60 * 60

console.log(totalVisit / totalSeconds / machineCount) // 6.944444444444445
复制代码

TPSはたったの7これでも、既知の数値が除外されるわけではありません。具体的なレポート ロジックについては、次の図を参照してください。

データを宝くじステーションに引き渡す戦略 (データのハイジャックを避けるため)

もちろん、すべての宝くじデータを宝くじ所に渡すことはできませんので、すべてのデータを階層化する必要がありますが、他の宝くじ所が選んだ「特別に選んだデータ」こそが、階層的に配布したいデータなのです!「全データの乗っ取りをどう回避するか」という問題もこれで解決します  !

では、データをどのように階層化すればよいのでしょうか?

つまり、陝西省西安宝くじ所のチケット購入情報を山西省太原まで、上海のチケット購入情報を江蘇蘇州まで同期させます!もちろん、2 つの場所間のデータの交換だけでなく、ロジックがより複雑になるなど、考慮すべき点は数多くありますが、通常、考慮すべき点は次のとおりです。

  • データの同期は困難で、中国南部から北部への同期など、地域を越えた同期はサーバーに大きな負荷がかかります

  • データの類似性の程度、2 つの場所のデータの歴史的類似性が大きく異なる場合、最終的にはこのベット数をより多く購入してもらいたいため、取材の目的を達成できません。

  • 新疆ウイグル自治区やその他の場所では、ネットワークの問題によりデータ同期の時間差が他の場所よりもはるかに遅いため、数値が欠落します。その場合、これらの場所のデータを上海などのより繁栄した地域に同期する必要がありますが、これは最初の2点とは矛盾しているように思えますが、

以上です、あまり話してもよくわかりません。あるいは、まだわかりませんが、この分野でより強力な偉人がいる場合は、彼がアイデアを提供してくれるでしょう。乱数がどのようになるかを見てみましょう:

必要な 2 つのベットをランダムに取得してみます。

function random(count) {
  let result = []
  for (let i = 0; i < count; i++) {
    const index = Math.floor(Math.random() * firstDataList.length)
    console.log(firstDataList[index])
    result.push(letterToCode(firstDataList[index]))
  }
  return result
}

console.log(random(2))
复制代码

わかった、宝くじが当たると思う?ハハハ、それはまだ可能です、読み続けてください!

意図的に 2 つの賭けを選んだ

私は典型的な「数字を守る人」です。毎日、自分で計算したいくつかの数字が記載された宝くじを購入しています。宝くじは当たりますか? (現在は選択されていません)

上記の説明によれば、「番号キーパー」が購入した番号は、システム内にデータがあるかどうかを判断する必要があり、データが存在する場合はレポートがトリガーされず、データが存在しない場合はレポートがトリガーされることがわかります。をシステムに報告すると、システムは現在の番号を配布します。隣接する都市または同様のデータを持つ都市では、現在の番号をより多くの人が購入できることが予想されます。より多くの番号を購入すると、賞品を獲得する確率が高くなります。低くなります!

ただし、宝くじに当選する確率は、ランダムに選択した場合よりも意図的に選択した場合の方が高くなりますが、それほど高いわけではありません。

一等賞が欲しい

宝くじの1等は統計に基づいていますが、宝くじセンターに空き番号があっても、その空き番号によって発生する2等〜6等の当選数を考慮する必要があり、非常に多くのデータが必要となります。たくさんの計算が必要です。時間があるので、どうやってシミュレーションすればよいでしょうか?

500,000 枚の宝くじを購入した場合の状況をシミュレーションしてみます。空の番号、重複購入、複数購入などの可能性があります。支払総額を計算してみます。

宝くじの当選ルールは以下の通りで、当面は変動賞は考慮せず、1等、2等ともに定額とさせていただきます。

  1. 6 + 1 の 1 等ボーナス 500 万

  2. 6 + 0 2 等ボーナス 300,000

  3. 5+1 3等賞ボーナス3000元

  4. 5 + 0 または 4 + 1 4 番目の賞品ボーナスは 200 元です

  5. 4 + 0 または 3 + 1 5 等のボーナスは 10 元です

  6. 2 + 1 または 1 + 1 または 0 + 1 は 6 等のボーナス 5 元です

この規則に従って、最初に報酬の関数を書くことができます。

/**
 * @param {String[]} target ['01', '02', '03', '04', '05', '06', '07']
 * @param {String[]} origin ['01', '02', '03', '04', '05', '06', '07']
 * @returns {Number} 返回当前彩票的中奖金额
 */
function compareToMoney(target, origin) {
  let money = 0
  let rightMatched = target[6] === origin[6]
  // 求左边六位的交集数量
  let leftMatchCount = target.slice(0, 6).filter(
    c => origin.slice(0,6).includes(c)
  ).length

  if (leftMatchCount === 6 && rightMatched) {
    money += 5000000
  } else if (leftMatchCount === 6 && !rightMatched) {
    money += 300000
  } else if (leftMatchCount === 5 && rightMatched) {
    money += 3000
  } else if (leftMatchCount === 5 && !rightMatched) {
    money += 200
  } else if (leftMatchCount === 4 && rightMatched) {
    money += 200
  } else if (leftMatchCount === 4 && !rightMatched) {
    money += 10
  } else if (leftMatchCount === 3 && rightMatched) {
    money += 10
  } else if (leftMatchCount === 2 && rightMatched) {
    money += 5
  } else if (leftMatchCount === 1 && rightMatched) {
    money += 5
  } else if (rightMatched) {
    money += 5
  }
  return money
}
复制代码

では、メリットを最大限に高めるには、次のような手順を実行する必要があります。

  • 当選番号のセットをランダムに生成する

  • 購入した数字ごとに、当選数字と一致するかどうかを確認し、ボーナス額を計算します。

  • 購入したすべての番号のボーナス額を合計する

  • 最適な当選番号が見つかるまでこのプロセスを繰り返します

ランダムな当選番号は非常に重要で、全体のデータを計算する速度を決定するため、次の手順に従って取得します。

  • 購入数に従ってすべての数字を並べ替えます (実際、ここでの実際のシーンでは、当選数字の分布傾向をより正確に考慮する必要があります)

  • 空の数値からクエリを開始し、順番に計算を実行します

まず購入データをシミュレートします。

function getRandomCode(count = 500000) {
  const arrRed = Array.from({ length: 33 }, (_, index) => (index + 1).toString().padStart(2, '0'))
  // generateCombinations 是我们上面定义过的函数
  const arrRedResult = generateCombinations(arrRed, 6, count)

  const result = []
  let blue = 1
  arrRedResult.forEach(line => {
    result.push([...line, (blue++).toString().padStart(2, '0')])
    if (blue > 16) {
      blue = 1
    }
  })

  return result
}

function randomPurchase() {
  const codes = getRandomCode()
  const result = []
  for (let code of codes) {
    let count = Math.floor(Math.random() * 50)
    result.push({
      code,
      count,
    })
  }
  return result
}

console.log(randomPurchase())
复制代码

次のようなデータ構造が得られます。これは統計に便利です。

[
  {
    code: [
      '01', '02',
      '03', '04',
      '05', '10',
      '05'
    ],
    count: 17
  },
  {
    code: [
      '01', '02',
      '03', '04',
      '05', '11',
      '06'
    ],
    count: 4
  }
]
复制代码

次に、非常に単純な統計です。ロジックは非常に単純ですが、大量のデータを使用する宝くじの場合は時間がかかります。

// 空号在前,购买数量越多越靠后
const purchaseList = randomPurchase().sort((a, b) => a.count - b.count)
const bonusPool = []

for (let i = 0; i < purchaseList.length; i++) {
  // 假设这就是一等奖,那么就需要计算其价值
  const firstPrize = purchaseList[0]
  let totalMoney = 0

  for (let j = 0; j < purchaseList.length; j++) {
    // 与一等奖进行对比,对比规则是参照彩票中奖规则
    const money = compareToMoney(purchaseList[j].code, firstPrize.code) * purchaseList[j].count
    totalMoney += money
  }

  bonusPool.push({
    code: firstPrize.code,
    totalMoney,
  })
}

const result = bonusPool.sort((a, b) => a.totalMoney - b.totalMoney)
// 至于怎么挑,那就随心所欲了
console.log(result[0].code, result[0].totalMoney)
复制代码

最終的な1位の決め方については自由ですが、上記のアルゴリズムは私のM1チップで計算するのに10分近くかかります。より強力なマシンとより強力なアルゴリズムがあれば、この時間も可能です。短くなりました。始めないでください、疲れているので、そのままにしてください!

黄梁は夢

結局のところ、それは黄梁の夢であり、最終的には私が人生に戻って頑張らなければなりません!しかし、後で別の賭けを購入するのはどうでしょうか?

宝くじシステムは純粋に推測的なものであり、類似点は存在しません。

おすすめ

転載: blog.csdn.net/qq_48652579/article/details/131054547