Multiplicación de nim

17

Antecedentes

Si hace mucho golf de código, es probable que conozca la operación XOR bit a bit . Dados dos enteros, da otro entero con 1s en los bits donde las dos entradas difieren. Entonces, por ejemplo 1010 XOR 0011 = 1001,.

Resulta ser muy útil en la teoría de juegos, donde se conoce mejor como la "suma nim". Si tiene la suma de dos juegos (es decir, está haciendo movimientos en un juego a la vez), el valor de la posición es la suma mínima de los valores de las posiciones en cada juego individual.

Pero podemos llevar esto un paso más allá. Con la suma de nim y una definición apropiada de multiplicación de nim , podemos formar un campo a partir de los enteros no negativos. Entonces, el desafío es la multiplicación de nim de golf.

Definición

La multiplicación de Nim obedece las siguientes reglas:
El producto nim de un Fermat 2-power n = (2 ^ (2 ^ k)) con cualquier número menor es el producto ordinario.
El producto nim de un Fermat 2-power n consigo mismo es 3n / 2.
La multiplicación de nim se distribuye sobre la suma de nim.
La multiplicación de nim es conmutativa y asociativa (como lo es la adición de nim).
La identidad multiplicativa es 1 (y la identidad aditiva es 0).

Cualquier número entero no negativo se puede escribir como la suma nim de potencias distintas de dos, y cualquier potencia de dos se puede escribir como el producto de números Fermat distintos, por lo que esto es suficiente para definir la multiplicación nim para todos los números enteros no negativos.

Ejemplo

Todo fue bastante abstracto, así que analicemos un ejemplo. Usaré +para denotar la suma de nim (XOR) y *para la multiplicación de nim.

6 * 13
= (4 + 2) * (8 + 4 + 1)
= (4 + 2) * ((4 * 2) + 4 + 1)
= (4 * 4 * 2) + (4 * 2 * 2) + (4 * 4) + (4 * 2) + (4 * 1) + (2 * 1)
= (6 * 2) + (4 * 3) + 6 + 8 + 4 + 2
= ((4 + 2) * 2) + 12 + 6 + 8 + 4 + 2
= (4 * 2) + (2 * 2) + 12 + 6 + 8 + 4 + 2
= 8 + 3 + 12 + 6 + 8 + 4 + 2
= 15

Casos de prueba adicionales

4, 4 -> 6
4, 3 -> 12
4, 7 -> 10
2, 4 -> 8
2, 3 -> 1
1, 42 -> 42

Desafío

Escriba un programa o función que, dados dos enteros no negativos en cualquier forma conveniente, calcule su producto nim.

Este es el , por lo que gana la presentación más corta.


fuente
1
En caso de que no esté claro para los lectores, esto es diferente de la multiplicación XOR (sin carga), por lo que no es un duplicado de ese desafío.
xnor
1
Tablas de multiplicación de nim en OEIS: A051775 , A051776 , A051910 , A051911 .
Arnauld
También tenga en cuenta que no hay una forma intuitiva de comprender la multiplicación de nimber (de acuerdo con esa publicación).
user202729
Los números de Fermat tienen la forma 2 ^ (2 ^ k) +1, por lo que lo que llama un número de Fermat es en realidad uno menos.
Kelly Lowder
@KellyLowder Sí, es realmente un Fermat 2-power.

Respuestas:

8

Nim , 120 bytes

proc f(a,b:int):int=
 var s={0..a*b}
 for i in 0..<a*b:s=s-{f(i%%a,i/%a)xor f(a,i/%a)xor f(i%%a,b)}
 for i in s:return i

Pruébalo en línea!

OK, esto puede ser una locura, pero alguien tuvo que hacer la multiplicación de Nim en Nim ...

Este es un algoritmo estándar de Wikipedia. El problema es que no sé el idioma, así que tuve que aprender los conceptos básicos sobre la marcha. En particular, me sorprendió eso -=y minno funcionó para conjuntos, y la mejor manera que pude encontrar para extraer el mínimo fue usar el iterador y devolver el primer valor. Con suerte, los expertos de Nim me ayudarán a mejorar esto.

Kirill L.
fuente
2
Me preguntaba cuándo alguien intentaría esto.
4

Jalea , 16 bytes

p’ß/;ß"^/ʋ€ṭ‘ḟ$Ṃ

Utiliza la fórmula recursiva xy = mex ({ay ⊕ xb ⊕ ab: a <x, b <y}) para la multiplicación de nimber .

Pruébalo en línea!

Cómo funciona

p’ß/;ß"^/ʋ€ṭ‘ḟ$Ṃ  Main link. Left argument: x. Right argument: y.

p                 Cartesian product; yield the array of all pairs [a, b] such that
                  0 < a ≤ x and 0 < b ≤ y.
 ’                Decrement, changing the conditions to 0 ≤ a < x and 0 ≤ b < y.
          ṭ       Tack; yield [y, x].
        ʋ€        Combine the four links to the left into a dyadic chain. Call it
                  with right argument [y, x] and each of the [a, b] as left one.
  ß/                  Reduce [a, b] by the main link, computing the product ab.
     ß"               Zip [a, b] with [y, x] using the main link, computing the
                      array of products [ay, xb].
    ;                 Concatenate, yielding [ab, ay, xb].
       ^/             Reduce by bitwise XOR, yielding ab ⊕ ay ⊕ xb.
                  All that's left is to compute the minimum excluded (mex) non-
                  negative integer.
             $    Combine the two links to the left into a monadic chain.
           ‘          Increment the XOR sums.
            ḟ         Filterfalse; remove all incremented sums that appear in the
                      original sums.
              Ṃ  Take the minimum if the resulting array is non-empty or yield 0.
                 If x = 0 or y = 0, the array of sums is empty and Ṃ yields 0.
                 If x > 0 and y > 0, since 0 is among the sums, this finds the
                 smallest non-sum n+1 such that n ≥ 0 is a sum.
                 In either case, Ṃ yields xy.
Dennis
fuente
4

CGSuite ,52 39 22 bytes

(a,b)->a.NimProduct(b)

No me di cuenta que tiene este "procedimiento" incorporado y anónimo.

Versión original, 36 bytes:

(a,b)->*a.ConwayProduct(*b).NimValue

O 25 bytes si la entrada / salida podría ser nimbers:

(a,b)->a.ConwayProduct(b)

Bueno, esperaba *a**b/ a*btrabajar, pero no es así.

jimmy23013
fuente
Definitivamente la herramienta adecuada para el trabajo.
3

Pyth , 21 bytes

Mf-TsmmxxgkdgkHgGdGH0

Demostración

Utiliza la formulación mínima de elementos excluidos de la multiplicación de nim, como se indica aquí .

Se utilizan dos mapas anidados para iterar sobre todos los valores más pequeños ( mm ... GH), luego los resultados se aplanan ( s). La parte inteligente viene con f-T ... 0, donde iteramos sobre enteros hacia arriba desde 0 para encontrar el primero que no figura en el conjunto mencionado anteriormente. Al hacerlo de esta manera, no necesitamos calcular un límite superior de iteración, ahorrando unos pocos bytes.

Al final, la función gcalcula el producto nim.

isaacg
fuente
3

JavaScript (ES6), 142128 bytes

f=(x,y,z=Math.log2,v=x&-x,t=z(x),u=z(y),s=t&u,r=s&-s)=>x<2|y<2?x*y:x>v?f(v,y)^f(x^v,y):y&y-1?f(y,x):r?f(f(x>>r,y>>r),3<<r-1):x*y
<div oninput=o.textContent=f(x.value,y.value)><input id=x><input id=y><pre id=o>

El primer paso es dividir ambos xy yen un XOR de poderes de 2, tomar sus productos nim por pares y luego XOR los resultados (porque el producto nim se distribuye sobre XOR). Una vez que hemos recursed al caso de xy yambas potencias de 2, observamos que los poderes de Fermat se multiplican entre sí utilizando la aritmética ordinaria, por lo que puede, por tanto, factorizar xy yen potencias de Fermat. Si xy yno compartimos un poder de Fermat, podemos revertir el proceso y simplemente regresar x * y. Sin embargo, si comparten un poder de Fermat, entonces dividimos ambos xy ypor ese poder, calculamos el producto nim, luego tomamos el producto nim con el cuadrado nim de ese poder de Fermat. Sin golf:

function nimprod(x, y) {
    if (x < 2 || y < 2) return x * y;
    var v = x & -x;
    if (x > v) return nimprod(v, y) ^ nimprod(x ^ v, y); // nimprod distributes over ^
    if (y & (y - 1)) return nimprod(y, x); // x is a power of 2 but y is not
    var t = Math.log2(x);
    var u = Math.log2(y);
    var s = t & u;
    if (!s) return x * y; // x and y do not share a Fermat power
    var r = s & -s;
    return nimprod(nimprod(x >> r, y >> r), 3 << r - 1); // square the Fermat power
}
Neil
fuente
1

Wolfram Language (Mathematica) , 81 bytes

x_±y_:=Min@Complement[Range[0,x*y],##&@@Array[BitXor[x±#2,#±y,±##]&,{x,y},0]]

Pruébalo en línea!

Usando la fórmula:

αβ=mex({αβ+αβ+αβ:α<α,β<β}).

alephalpha
fuente