La verdad sobre la tasa de ganancias de la lotería: use JavaScript para ver a través del algoritmo aleatorio detrás de la lotería

Originalmente, este artículo estaba destinado a llamarse "Si yo fuera un desarrollador de sistemas de lotería", pero si lo piensa, si cita demasiado JavaScript en el artículo, no será tan puro. Después de todo, es solo mi deseo. pensando, y el desarrollo de la lotería no está completo Como se menciona en este artículo, si es engañoso, no valdrá la pena.

Así que simplemente se llama "La verdad de la tasa ganadora de la lotería: uso de JavaScript para ver a través del algoritmo aleatorio detrás de la lotería" , que es un poco más claro. Permítanme declarar que el sistema de lotería real no se desarrolla de esta manera, ni tampoco tiene reglas obvias Se debe confiar en la imparcialidad de la lotería , ¡aunque no se base en el azar!

chisme

Últimamente me he obsesionado con la lotería, imaginé que si puedo hacerme rico, también puedo llevar a mi familia a la "ascensión al cielo".

Cuando compré un boleto de lotería, también lo pensé durante mucho tiempo. ¿Qué tipo de números pueden destacar entre los 17 millones de apuestas? Probé números aleatorios, los seleccioné cuidadosamente e intenté encontrar patrones regulares. Incluso usé rastreadores. para encontrarlos ¡Las estadísticas son ridículas!

Nuestro sistema de lotería predeterminado se basa en estadísticas para lograr el sorteo del primer premio, por lo que, por supuesto, el primer premio en la historia debe ser la apuesta con la tasa estadística más baja en el período actual, por lo que, al principio, pensé de esta manera:

  1. Obtenga todos los números de lotería ganadores en la historia

  2. Usa el código para contar los tiempos ganadores de todos los números

  3. Ordenar por el número menos probable

  4. Forme algunos números nuevos a su vez

Es solo una forma de desahogar tus ganas de ganar dinero. No es exagerado llamarlo caprichoso. Es mucho hablar, ¡jaja!

Ya he practicado la idea anterior, y se ha utilizado durante casi un año, ¡pero es inútil! ¡no utilice! Por supuesto, también puedes intentarlo, si ganas, ¡enhorabuena, eres el elegido!

reglas de la lotería

Nuestras reglas de lotería aquí están unificadas usando las reglas de "bola de dos colores" para ilustrar, y las reglas para su compra son las siguientes:

  1. La bola roja es seis, la opción se selecciona de 1 a 33, no repetible

  2. La bola azul es una, y las opciones se seleccionan de 1 a 16

  3. Un total de siete bolas bicolores rojas y azules forman una apuesta

El primer premio generalmente selecciona una apuesta entre todas las apuestas compradas.Esta apuesta puede ser comprada por varias personas, o puede ser un múltiplo de la apuesta comprada por una persona.

Aproximadamente, la fórmula para calcular las probabilidades de ganar una lotería es la siguiente:

Use la fórmula del número de combinación para calcular, la fórmula del número de combinación npara sacar elementos de los elementos es:k

C(kn)=n!k!(n−k)!C\binomial{k}{n}=\frac{n!}{k!(nk)!}C(nk)=k!(n−k )!¡norte!

De acuerdo con la fórmula, podemos escribir fácilmente un algoritmo simple:

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
复制代码

Por lo tanto, se puede concluir que las probabilidades de ganar el premio mayor de la bola de dos colores son: 117721088\frac{1}{17721088}177210881

la cantidad de datos

A través del algoritmo anterior, sabemos que el número total de apuestas en la lotería es  17721088, entonces, ¿cuál es el tamaño de los datos compuestos por tantos números de apuestas?

Mediante un simple cálculo, un billete de lotería se puede representar con números 14. Por ejemplo  01020304050607, en el sistema operativo, el tamaño de esta cadena de números es  14B, y se sabe aproximadamente que si todos los billetes de lotería están en un archivo, entonces este tamaño del archivo es:

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

Terrible cantidad, se puede ser menor? ¡Estudiemos el algoritmo de compresión!

01Este número ocupa dos bytes en la memoria, que es 2 B. Si lo reemplazamos  01 con minúsculas  a , entonces su capacidad puede convertirse en 1 B, ¡y la capacidad total puede reducirse a la mitad!

De esta forma, nuestro número de apuesta especial anterior  01020304050607 se puede expresar como  abcdefg !

Este es el principio más básico del algoritmo de compresión. Hay muchos tipos de algoritmos de compresión, que se dividen aproximadamente en compresión con pérdida y compresión sin pérdida. Para el contenido de nuestra clase de datos, generalmente elegimos la compresión sin pérdida.

  • Algoritmos de compresión con pérdida: estos algoritmos pueden descartar cierta información al comprimir datos, pero generalmente logran relaciones de compresión más altas sin afectar el uso real, los más comunes son los algoritmos de compresión de imagen, audio y video.

  • Algoritmos de compresión sin pérdidas: estos algoritmos no descartan ninguna información, logran la compresión buscando patrones repetidos en los datos de entrada y utilizando símbolos más cortos para representarlos. Los algoritmos de compresión sin pérdida se utilizan a menudo para texto, código, archivos de configuración y otros tipos de datos.

En primer lugar, preparemos primero algunos datos de prueba, usamos el siguiente algoritmo de generación de números de combinación simple para obtener 1000 números de combinación:

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
}
复制代码

A continuación, necesitamos generar 1000 bolas de dos colores. Las bolas rojas se seleccionan del 1 al 33 y las bolas azules se seleccionan del 1 al 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
}
复制代码

Ponemos el contenido de lotería adquirido en un archivo para el siguiente paso:

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

De esta forma, obtenemos la primera versión del archivo, que es su tamaño de archivo:

Pruebe nuestro algoritmo de compresión preliminar, implementaremos las reglas recién establecidas, es decir, la sustitución de números por letras, con JavaScript, de la siguiente manera:

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)
复制代码

De esta manera, obtenemos un nuevo archivo de saludo, cuyo tamaño es el siguiente, ¡lo que simplemente confirma nuestra idea!

Si seguimos este algoritmo, podemos comprimir el archivo anterior a la mitad del tamaño, es decir , pero ¿es este el límite? No, como dijimos anteriormente, esta es solo la compresión más básica. A continuación, ¡probemos un método más sutil! 118.301025390625MB

manera más sutil

Aquí no explicamos demasiado el principio del algoritmo de compresión. Si está interesado, puede encontrar artículos similares para leer por sí mismo. Dado que la calidad de los artículos en Internet es desigual, ¡no lo recomendaré!

Lo que debemos entender aquí es que lo que estamos estudiando es un sistema de lotería, por lo que su compresión de datos debe tener las siguientes características:

  • Tiene las características de no pérdida de datos, es decir, compresión sin pérdidas

  • La tasa de compresión debe ser lo más pequeña posible, porque los archivos transferidos pueden ser muy grandes, como en el ejemplo que dimos anteriormente.

  • Facilitar la transmisión de información, es decir, soportar solicitudes HTTP

Los estudiantes que a menudo trabajan en el front-end deben saber que un parámetro común en el encabezado de la solicitud HTTP  también elegirá convertir archivos de recursos para  su distribución content-encoding: gzipen términos de optimización del proyecto  . gzipEn el uso diario, también solemos depender de  WebpacklibreríasRollup  como , o  nginx la compresión completa de recursos a través de un servidor de red, lo que gzip no solo reduce mucho el contenido a enviar, sino que también permite al cliente descomprimir y acceder a los archivos fuente sin pérdidas.

Entonces, ¿podemos usar  gzip para completar la compresión? La respuesta es sí, Node.js nos proporciona  zlib una biblioteca de herramientas y una función de compresión correspondiente:

const zlib = require('zlib')

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

El resultado es:

¡Hemos hecho un proceso de compresión de 14 KB -> 3 KB! ¿No es interesante? Pero, de nuevo, ¿es posible ser más pequeño? ¡seguro!

content-encoding El encabezado de respuesta es generalmente la información de configuración del servidor para el formato de codificación de respuesta de recursos devuelto. Hay tres valores comunes:

  • gzip Formato de compresión universal compatible con todos los navegadores

  • brotli Un nuevo formato de compresión con  gzip un mejor rendimiento de compresión y una tasa de compresión más pequeña, que no es compatible con los navegadores antiguos

  • deflate Por alguna razón, no se usa mucho, y existe un formato de compresión basado en este algoritmo  zlib , pero no se usa mucho.

Los formatos de compresión admitidos por los navegadores no se limitan a estos, pero hemos enumerado los más utilizados, intentemos usar estos tres formatos de compresión:

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))
复制代码

Podemos ver que la tasa de compresión de deflate gzip es comparable y, sorprendentemente, brotlila compresión de y ha alcanzado un sorprendente 1 KB ¿No es esto lo que queremos?

¿Podría ser más pequeño? Jajajaja, por supuesto, si no se considera la compatibilidad con HTTP, podemos usar un  7-zip algoritmo de compresión con una tasa de compresión más baja para completar la compresión y luego usar el cliente para realizar la descompresión manual. ¡Pero hasta ahora, no hemos hecho un trabajo más importante!

Antes de eso, primero debemos comprender el proceso de descompresión. Si los datos se pierden después de la descompresión, ¡entonces la pérdida supera la ganancia!

// 执行解压操作
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
复制代码

Como se mencionó anteriormente, sabemos que aunque el efecto del algoritmo de compresión es sorprendente, ¡los datos descomprimidos aún no tienen pérdidas!

datos completos

¿Vamos a crear una nota de datos completa de 17721088 para probar la capacidad del algoritmo de compresión completo? ¡ Aquí usamos  brotli los  gzip algoritmos y para realizar pruebas de compresión por separado!

Primero, la función que generamos datos debe modificarse de la siguiente manera:

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
复制代码

A continuación, pasamos por la compresión inicial y la escribimos en un archivo:

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)
复制代码

Como esperábamos, después de la compresión preliminar, el tamaño del archivo alcanzó los 118 MB, pero su ocupación real es de 124 MB, que pertenece a la categoría de almacenamiento de la computadora. No lo discutiremos en este artículo. Los estudiantes interesados ​​​​pueden verificarlo por sí mismos. De acuerdo Calculado en bytes, su tamaño es:

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

Actualmente, está en línea con las expectativas. ¡Echemos un vistazo a las habilidades reales de los dos algoritmos de compresión!

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

De hecho, fue algo impactante. Aunque mis  brotli expectativas eran lo suficientemente altas, no hubiera pensado que podría comprimirse a un tamaño de solo 4M, pero para nosotros, esto es una bendición y tiene una gran ventaja para posteriores operaciones de distribución!

dos apuestas aleatorias

Las apuestas dobles aleatorias son muy comunes cuando se compran boletos de lotería en las estaciones de lotería, pero ¿qué sucede cuando intenta con números aleatorios?

Comencemos con la distribución de datos de lotería. En primer lugar, el diseño de la seguridad y la estabilidad de la distribución de datos de lotería está definitivamente fuera de toda duda, pero este no es un problema que debamos considerar en este momento. Lo que debemos resolver en este momento. es que si podemos lograr un menor grado de control de costes!

Suponiendo que usted es quien diseñó este sistema, ¿si controla la tasa ganadora de números aleatorios? ¡Mi respuesta es elegir del grupo de números existente!

Si cada estación de lotería puede obtener su grupo de números correspondiente, responda: ¡distribución de datos! Si se adopta el modo de distribución de datos, los aspectos a considerar son los siguientes:

  • cuando distribuir

  • Cómo hacer que los datos vuelvan a la fuente

  • Cómo evitar todos los secuestros de datos

  • La estrategia de entregar los datos a la estación de lotería.

Según información pública en 2021, el número de estaciones de lotería ha llegado a 200 000 (no verificado, sin valor de referencia), ¡suponemos que el número actual de estaciones de lotería es de 300 000!

cuando distribuir

Lo que sabemos es que la fecha límite para la compra de boletos de lotería es a las 20:00 horas, y el horario del sorteo es a las 21:15 horas, a partir de las 8:00 horas, el plan es el siguiente:

  1. De la biblioteca de lotería actual, organice los números según la probabilidad de ocurrencia de mayor a menor

  2. Se seleccionan las primeras 500.000 apuestas y se distribuyen a 300.000 puestos de lotería Se unifican los datos de los puestos de lotería en este momento

  3. Los datos se sincronizan cada hora, que son "datos especialmente seleccionados" de otras estaciones de lotería

¿Qué tan grande es el volumen de datos de 500,000 billetes? Pruébalo:

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))
复制代码

¡El tamaño de una sola imagen, lleva menos de 1 segundo obtener estos datos, descomprimirlos y sincronizarlos con la máquina de lotería!

Un ejemplo de descompresión es el siguiente:

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
复制代码

Cómo convertir los boletos de lotería obtenidos en forma de caracteres en números, tales  abcdefga como  ['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
}
复制代码

¿En cuanto a la distribución? Podemos referirnos a algunos conceptos existentes en el mercado para comparar, a continuación se muestra una estimación general del TPS de un servidor web, es decir, el número máximo de solicitudes que el servidor de lotería puede manejar en 1 segundo:

  • Bajo rendimiento: TPS por debajo de 50, adecuado para escenarios de aplicaciones de poco tráfico, como blogs personales, sitios web de pequeñas empresas, etc.

  • Rendimiento medio: el TPS está entre 50 y 500, adecuado para sitios web generales y escenarios de aplicaciones, como sitios web de comercio electrónico pequeños y medianos, redes sociales, etc.

  • Alto rendimiento: el TPS está entre 500 y 5000, adecuado para sitios web de alto tráfico y escenarios de aplicaciones, como grandes sitios web de comercio electrónico, sitios web de juegos, etc.

  • Rendimiento ultra alto: TPS está por encima de 5000, adecuado para sitios web y escenarios de aplicaciones de tráfico ultra alto, como sitios web de gigantes de Internet, juegos en línea, etc.

De acuerdo con este modelo, la sincronización de datos de la estación de lotería 500,000 se puede completar en 100 segundos. Por supuesto, todos, este es un modo independiente. Si desea realizar un servicio de lotería, el modo independiente es definitivamente imposible. Si Si desea mejorar el TPS, simplemente haga un clúster de servidores. Si hay 100 clústeres de servidores, ¡solo se tarda 1 segundo en procesar estas solicitudes! (¿Eres obstinado? ¡Por supuesto que puedes ser obstinado si tienes dinero!) (Estos datos se basan en la teoría y no proporcionan un valor de referencia)

Cómo hacer que los datos vuelvan a la fuente

¡muy simple! ¿Qué tipo de datos necesitamos obtener? ¡Los datos de lotería comprados directamente sin algoritmo aleatorio! ¡Es decir, esos viejos jugadores de lotería que "mantienen sus números" que escuchamos a menudo!

De manera similar, según consultas de los medios (no para referencia), el flujo de pasajeros de la estación de lotería es de 1 a 10 personas por hora, y el horario de funcionamiento es de 9:00 am a 9:00 pm Se espera que el flujo máximo de pasajeros sea ser 100 personas por día!

Entonces, el flujo total de pasajeros de todas las estaciones de lotería es 100 * 500000 = 50000000, que es aproximadamente 50 millones de personas-tiempo, aproximadamente el 50 % de los cuales pertenecen a "guardianes de números". Puede ser necesario excluir los números conocidos en las estaciones de lotería, pero aquí no lo tratamos primero, sino que primero hacemos todas las estimaciones, luego

El TPS máximo que necesita llevar el servidor es:

// 服务器集群数量
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 es solo 7 ! ! Esto todavía no descarta el número ya conocido.Para la lógica de informes específica, consulte la siguiente figura:

La estrategia de entregar los datos a la estación de lotería (para evitar el secuestro de datos)

Por supuesto, todos los datos de la lotería no se pueden entregar a la estación de lotería. Necesitamos superponer todos los datos. ¡Los "datos especialmente seleccionados" por otras estaciones de lotería son los datos que queremos distribuir jerárquicamente! ¡Esto también resolverá  el problema de "cómo evitar que todos los datos sean secuestrados"  !

Entonces, ¿cómo superponemos los datos?

En resumen, sincronizamos la información de compra de boletos de la estación de lotería Xi'an en Shaanxi con Taiyuan, Shanxi, y la información de compra de boletos de Shanghai con Suzhou, Jiangsu. Por supuesto, hay muchos puntos a considerar, no solo el intercambio de datos entre los dos lugares, sino que también la lógica es más complicada. Por lo general, los puntos a considerar son:

  • La sincronización de datos es difícil, la sincronización entre regiones ejerce una gran presión sobre el servidor, como la sincronización del sur de China con el norte de China

  • El grado de similitud de datos, si la similitud histórica de los datos de los dos lugares es muy diferente, no podrá lograr el propósito de cobertura, porque finalmente queremos que este número de apuesta se compre más veces.

  • La diferencia horaria de sincronización de datos, como Xinjiang y otros lugares, debido a problemas de red, es mucho más lenta que en otros lugares, por lo que se perderán números, entonces los datos de estos lugares deben sincronizarse con áreas más prósperas, como Shanghái, pero esto Parece ser contrario a los dos primeros puntos.

Eso es todo lo que he dicho, y realmente no entiendo si hablo demasiado. O todavía no lo he descubierto, si hay un tipo grande que es más poderoso en esta área, ¡puede dar ideas! Veamos cómo resulta el número aleatorio:

Intentemos obtener aleatoriamente las dos apuestas que necesita:

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))
复制代码

Bien, ¿crees que puedes ganar la lotería? Jajaja, todavía es posible, ¡sigue leyendo!

Escogió deliberadamente dos apuestas

Soy una típica persona "guardadora de números". Todos los días compro un boleto de lotería con algunos números que calculé yo mismo. ¿Puedo ganar la lotería? (Actualmente no seleccionado)

De acuerdo con la descripción anterior, debemos saber que el número comprado por el "guardián del número" necesita juzgar si hay datos en el sistema. Si existen, el informe no se activará. Si los datos no existen, se se informará al sistema, y ​​el sistema distribuirá el número actual. Para ciudades adyacentes o ciudades con datos similares, se espera que más personas puedan comprar el número actual. Si se compran más números, la probabilidad de ganar el premio será más bajo!

Sin embargo, la probabilidad de ganar una lotería es mayor si la elige deliberadamente que si la elige al azar, pero no es mucho mayor.

quiero el primer premio

El primer premio de la lotería se basa en estadísticas. Incluso si hay números vacíos en el centro de lotería, se debe considerar la cantidad de premios segundo a sexto generados por los números vacíos. Esta es una gran cantidad de datos y requiere un muchos cálculos Tiempo, entonces, ¿cómo lo simulamos?

Tomemos 500,000 boletos de lotería y simulemos la situación cuando se compran estos boletos de lotería. Puede haber números vacíos, compras repetidas o compras múltiples, etc. ¡Intente calcular la cantidad total que debemos pagar!

Las reglas para ganar la lotería son las siguientes, no consideraremos los premios flotantes por el momento, y daremos cantidades fijas tanto al primer premio como al segundo premio:

  1. 6 + 1 primer premio bono de 5 millones

  2. 6 + 0 bono segundo premio de 300.000

  3. 5 + 1 bonificación del tercer premio de 3000 yuanes

  4. 5 + 0 o 4 + 1 La bonificación del cuarto premio es de 200 yuanes

  5. 4 + 0 o 3 + 1 La bonificación del quinto premio es de 10 yuanes

  6. 2 + 1 o 1 + 1 o 0 + 1 es la bonificación del sexto premio de 5 yuanes

De acuerdo con esta regla, primero podemos escribir la función de recompensa:

/**
 * @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
}
复制代码

Entonces, cómo maximizar los beneficios, los pasos deberían ser así:

  • Genera aleatoriamente un conjunto de números ganadores

  • Para cada número comprado, verifique si coincide con el número ganador y calcule su monto de bonificación

  • Sume los montos de bonificación para todos los números comprados

  • Repita este proceso hasta encontrar el número ganador óptimo

El número ganador aleatorio es muy importante, determina la velocidad a la que podemos calcular los datos totales, por lo que seguimos los siguientes pasos para obtenerlo:

  • Ordene todos los números según el número de compras (de hecho, la escena real aquí debería considerar la tendencia de distribución de los números ganadores para ser más precisa)

  • Inicie la consulta desde el número vacío y realice los cálculos secuencialmente

Primero simular nuestros datos de compra:

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())
复制代码

Obtendremos una estructura de datos similar a la siguiente, que es conveniente para las estadísticas:

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

Luego, son estadísticas muy simples, la lógica es muy simple, pero para la lotería con una gran cantidad de datos, ¡lleva tiempo!

// 空号在前,购买数量越多越靠后
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)
复制代码

En cuanto a cómo elegir el primer premio final, puede hacer lo que quiera, pero el algoritmo anterior tarda casi 10 minutos en calcularse en mi chip M1. Si hay una máquina más poderosa y un algoritmo más poderoso, este tiempo también puede ser acortado ¡No empieces, estoy cansado, que así sea!

Huangliang un sueño

Después de todo, es un sueño de Huangliang, ¡y al final tengo que volver a la vida y trabajar duro! Pero quién sabe, ¿qué tal comprar otra apuesta más tarde?

¡El sistema de lotería es puramente especulativo y no puede haber similitudes!

Supongo que te gusta

Origin blog.csdn.net/qq_48652579/article/details/131054547
Recomendado
Clasificación