Quanto in alto puoi andare? (Una sfida di codifica + algoritmi)


34

Ora che tutti hanno sviluppato la loro (spesso sorprendente) esperienza di programmazione a basso livello per quanto è lento Python? (O quanto è veloce la tua lingua?) E Quanto è lento Python (parte II)? è tempo di una sfida che aumenterà anche la tua capacità di migliorare un algoritmo.

Il seguente codice calcola un elenco di lunghezza 9. La posizione inell'elenco conta il numero di volte in cui isono stati trovati almeno zeri consecutivi durante il calcolo dei prodotti interni tra Fe S. Per fare esattamente questo, scorre tutte le possibili liste Fdi lunghezza ne liste Sdi lunghezza n+m-1.

#!/usr/bin/python
import itertools
import operator

n=8
m=n+1
leadingzerocounts = [0]*m
for S in itertools.product([-1,1], repeat = n+m-1):
    for F in itertools.product([-1,1], repeat = n):
        i = 0
        while (i<m and sum(map(operator.mul, F, S[i:i+n])) == 0):
            leadingzerocounts[i] +=1
            i+=1
print leadingzerocounts

L'output è

[4587520, 1254400, 347648, 95488, 27264, 9536, 4512, 2128, 1064]

Se aumenti n a 10,12,14,16,18,20 usando questo codice molto rapidamente diventa troppo lento.

Regole

  • La sfida è quella di fornire l'output corretto per una n maggiore possibile. Solo i valori pari di n sono rilevanti.
  • Se c'è un pareggio, la vittoria va al codice più veloce sulla mia macchina per il più grande n.
  • Mi riservo il diritto di non testare il codice che richiede più di 10 minuti.
  • Puoi modificare l'algoritmo nel modo che preferisci purché fornisca l'output corretto. In effetti dovrai cambiare l'algoritmo per fare progressi decenti verso la vittoria.
  • Al vincitore verrà assegnata una settimana dal momento in cui è stata posta la domanda.
  • La ricompensa verrà assegnata quando è dovuta, ovvero poco dopo quando verrà assegnato il vincitore.

La mia macchina I tempi verranno eseguiti sulla mia macchina. Questa è un'installazione ubuntu standard su un processore a otto core AMD FX-8350. Questo significa anche che devo essere in grado di eseguire il tuo codice. Di conseguenza, utilizzare solo software gratuito facilmente disponibile e includere istruzioni complete su come compilare ed eseguire il codice.

Stato .

  • C . n = 12 in 49 secondi da @Fors
  • Java . n = 16 in 3:07 di @PeterTaylor
  • C ++ . n = 16 in 2:21 di @ilmale
  • Rpython . n = 22 in 3:11 di @primo
  • Java . n = 22 in 6:56 da @PeterTaylor
  • Nimrod . n = 24 in 9:28 secondi da @ReimerBehrends

Il vincitore è stato Reimer Behrends con un ingresso a Nimrod!

Come controllo, dovrebbe essere l'uscita per n = 22 [12410090985684467712, 2087229562810269696, 351473149499408384, 59178309967151104, 9975110458933248, 1682628717576192, 284866824372224, 48558946385920, 8416739196928, 1518499004416, 301448822784, 71620493312, 22100246528, 8676573184, 3897278464, 1860960256, 911646720, 451520512, 224785920, 112198656, 56062720, 28031360, 14015680].


La competizione è finita ma ... offrirò 200 punti per ogni invio che aumenta n di 2 (entro 10 minuti sul mio computer) finché non esaurisco i punti. Questa offerta è aperta per sempre .


1
"Mi riservo il diritto di non testare il codice che richiede più di qualche minuto." > Dovresti specificare un orario esatto sulla tua macchina, altrimenti questa domanda non avrà un criterio vincente oggettivo.
pastebin.com slash 0mr8spkT

14
Io amo queste sfide "aumentare la mia velocità". Se stai costruendo un prodotto commerciale con questi, avrai un prodotto velocissimo e sei anche un genio del male .
Rainbolt,

1
Forse un titolo più informativo attirerebbe l'attenzione su questo?
TheDoctor

8
Se continuiamo a fare questo tipo di sfida, penso che dovremmo almeno tentare di risolvere un problema diverso per mantenerlo interessante (non una variazione sullo stesso problema con specifiche aggiuntive).
Grove

2
@Claudiu la sua CPU ha 8 core fisici, ma le unità fetch / decode e FPU sono condivise tra i core. Quindi, quando il collo di bottiglia si trova in una di quelle aree, si comporta più come un quadcore. Abusa della logica di numero intero ed evita grandi dimensioni di codice ed è più simile a un 8 core.
Stefan,

Risposte:


20

Nimrod (N = 22)

import math, locks

const
  N = 20
  M = N + 1
  FSize = (1 shl N)
  FMax = FSize - 1
  SStep = 1 shl (N-1)
  numThreads = 16

type
  ZeroCounter = array[0..M-1, int]
  ComputeThread = TThread[int]

var
  leadingZeros: ZeroCounter
  lock: TLock
  innerProductTable: array[0..FMax, int8]

proc initInnerProductTable =
  for i in 0..FMax:
    innerProductTable[i] = int8(countBits32(int32(i)) - N div 2)

initInnerProductTable()

proc zeroInnerProduct(i: int): bool =
  innerProductTable[i] == 0

proc search2(lz: var ZeroCounter, s, f, i: int) =
  if zeroInnerProduct(s xor f) and i < M:
    lz[i] += 1 shl (M - i - 1)
    search2(lz, (s shr 1) + 0, f, i+1)
    search2(lz, (s shr 1) + SStep, f, i+1)

when defined(gcc):
  const
    unrollDepth = 1
else:
  const
    unrollDepth = 4

template search(lz: var ZeroCounter, s, f, i: int) =
  when i < unrollDepth:
    if zeroInnerProduct(s xor f) and i < M:
      lz[i] += 1 shl (M - i - 1)
      search(lz, (s shr 1) + 0, f, i+1)
      search(lz, (s shr 1) + SStep, f, i+1)
  else:
    search2(lz, s, f, i)

proc worker(base: int) {.thread.} =
  var lz: ZeroCounter
  for f in countup(base, FMax div 2, numThreads):
    for s in 0..FMax:
      search(lz, s, f, 0)
  acquire(lock)
  for i in 0..M-1:
    leadingZeros[i] += lz[i]*2
  release(lock)

proc main =
  var threads: array[numThreads, ComputeThread]
  for i in 0 .. numThreads-1:
    createThread(threads[i], worker, i)
  for i in 0 .. numThreads-1:
    joinThread(threads[i])

initLock(lock)
main()
echo(@leadingZeros)

Compila con

nimrod cc --threads:on -d:release count.nim

(Nimrod può essere scaricato qui .)

Questo viene eseguito nel tempo assegnato per n = 20 (e per n = 18 quando si utilizza solo un singolo thread, impiegando circa 2 minuti in quest'ultimo caso).

L'algoritmo utilizza una ricerca ricorsiva, potando l'albero di ricerca ogni volta che si incontra un prodotto interno diverso da zero. Abbiamo anche dimezzato lo spazio di ricerca osservando che per ogni coppia di vettori (F, -F)dobbiamo solo considerare uno perché l'altro produce gli stessi identici insiemi di prodotti interni (negando Sanche).

L'implementazione utilizza le strutture di metaprogrammazione di Nimrod per srotolare / allineare i primi livelli della ricerca ricorsiva. Ciò consente di risparmiare un po 'di tempo quando si utilizzano gcc 4.8 e 4.9 come backend di Nimrod e una discreta quantità di clang.

Lo spazio di ricerca potrebbe essere ulteriormente potato osservando che dobbiamo solo considerare i valori di S che differiscono in un numero pari delle prime posizioni N dalla nostra scelta di F. Tuttavia, le esigenze di complessità o memoria di ciò non si adattano per valori di grandi dimensioni di N, dato che il corpo del loop viene completamente ignorato in questi casi.

Tabulare in cui il prodotto interno è zero sembra essere più veloce rispetto all'utilizzo di qualsiasi funzionalità di conteggio dei bit nel loop. A quanto pare l'accesso al tavolo ha una località abbastanza buona.

Sembra che il problema dovrebbe essere suscettibile alla programmazione dinamica, considerando come funziona la ricerca ricorsiva, ma non esiste un modo apparente per farlo con una quantità ragionevole di memoria.

Esempi di output:

N = 16:

@[55276229099520, 10855179878400, 2137070108672, 420578918400, 83074121728, 16540581888, 3394347008, 739659776, 183838720, 57447424, 23398912, 10749184, 5223040, 2584896, 1291424, 645200, 322600]

N = 18:

@[3341140958904320, 619683355033600, 115151552380928, 21392898654208, 3982886961152, 744128512000, 141108051968, 27588886528, 5800263680, 1408761856, 438001664, 174358528, 78848000, 38050816, 18762752, 9346816, 4666496, 2333248, 1166624]

N = 20:

@[203141370301382656, 35792910586740736, 6316057966936064, 1114358247587840, 196906665902080, 34848574013440, 6211866460160, 1125329141760, 213330821120, 44175523840, 11014471680, 3520839680, 1431592960, 655872000, 317675520, 156820480, 78077440, 39005440, 19501440, 9750080, 4875040]

Ai fini del confronto dell'algoritmo con altre implementazioni, N = 16 impiega circa 7,9 secondi sulla mia macchina quando si utilizza un singolo thread e 2,3 secondi quando si utilizzano quattro core.

N = 22 impiega circa 15 minuti su una macchina a 64 core con gcc 4.4.6 come backend di Nimrod e trabocca in interi a 64 bit leadingZeros[0](probabilmente non quelli senza segno, non l'ho mai visto).


Aggiornamento: ho trovato spazio per un paio di miglioramenti in più. Innanzitutto, per un dato valore di F, possiamo enumerare con Sprecisione le prime 16 voci dei corrispondenti vettori, perché devono differire esattamente in N/2punti. Così abbiamo Precompute una lista di vettori di bit di dimensioni Nche hanno N/2bit set e utilizzare questi per ricavare la parte iniziale di Sda F.

In secondo luogo, possiamo migliorare la ricerca ricorsiva osservando che conosciamo sempre il valore di F[N](poiché MSB è zero nella rappresentazione dei bit). Questo ci consente di prevedere con precisione in quale ramo ricerchiamo dal prodotto interno. Sebbene ciò ci consentirebbe effettivamente di trasformare l'intera ricerca in un ciclo ricorsivo, ciò in realtà capita di rovinare un po 'la previsione del ramo, quindi manteniamo i livelli più alti nella sua forma originale. Risparmiamo ancora un po 'di tempo, principalmente riducendo la quantità di ramificazioni che stiamo facendo.

Per un po 'di pulizia, il codice ora utilizza numeri interi senza segno e li corregge a 64 bit (nel caso in cui qualcuno desideri eseguirlo su un'architettura a 32 bit).

Lo speedup complessivo è compreso tra un fattore di x3 e x4. N = 22 richiede ancora più di otto core per funzionare in meno di 10 minuti, ma su una macchina a 64 core è ora sceso a circa quattro minuti (con un numThreadsaumento di conseguenza). Tuttavia, non credo che ci sia molto più margine di miglioramento senza un algoritmo diverso.

N = 22:

@[12410090985684467712, 2087229562810269696, 351473149499408384, 59178309967151104, 9975110458933248, 1682628717576192, 284866824372224, 48558946385920, 8416739196928, 1518499004416, 301448822784, 71620493312, 22100246528, 8676573184, 3897278464, 1860960256, 911646720, 451520512, 224785920, 112198656, 56062720, 28031360, 14015680]

Aggiornato di nuovo, facendo uso di ulteriori riduzioni possibili nello spazio di ricerca. Funziona in circa 9:49 minuti per N = 22 sulla mia macchina quadcore.

Aggiornamento finale (penso). Migliori classi di equivalenza per le scelte di F, riducendo il tempo di esecuzione per N = 22 fino a 3:19 minuti 57 secondi (modifica: l'avevo accidentalmente eseguito con un solo thread) sulla mia macchina.

Questa modifica sfrutta il fatto che una coppia di vettori produce gli stessi zeri iniziali se uno può essere trasformato nell'altro ruotandolo. Sfortunatamente, un'ottimizzazione di basso livello abbastanza critica richiede che il bit superiore di F nella rappresentazione dei bit sia sempre lo stesso, e mentre si utilizza questa equivalenza si è ridotto di molto lo spazio di ricerca e si è ridotto il tempo di esecuzione di circa un quarto rispetto a uno spazio di stato diverso riduzione su F, il sovraccarico dall'eliminazione dell'ottimizzazione di basso livello più che compensato. Tuttavia, si scopre che questo problema può essere eliminato considerando anche il fatto che anche F che sono inverse l'una dall'altra sono equivalenti. Mentre questo ha aggiunto un po 'alla complessità del calcolo delle classi di equivalenza, mi ha anche permesso di conservare la suddetta ottimizzazione di basso livello, portando a una velocità di circa x3.

Un altro aggiornamento per supportare numeri interi a 128 bit per i dati accumulati. Per compilare con numeri interi a 128 bit, è necessario longint.nimda qui e compilare -d:use128bit. N = 24 richiede ancora più di 10 minuti, ma ho incluso il risultato di seguito per gli interessati.

N = 24:

@[761152247121980686336, 122682715414070296576, 19793870419291799552, 3193295704340561920, 515628872377565184, 83289931274780672, 13484616786640896, 2191103969198080, 359662314586112, 60521536552960, 10893677035520, 2293940617216, 631498735616, 230983794688, 102068682752, 48748969984, 23993655296, 11932487680, 5955725312, 2975736832, 1487591936, 743737600, 371864192, 185931328, 92965664]

import math, locks, unsigned

when defined(use128bit):
  import longint
else:
  type int128 = uint64 # Fallback on unsupported architectures
  template toInt128(x: expr): expr = uint64(x)

const
  N = 22
  M = N + 1
  FSize = (1 shl N)
  FMax = FSize - 1
  SStep = 1 shl (N-1)
  numThreads = 16

type
  ZeroCounter = array[0..M-1, uint64]
  ZeroCounterLong = array[0..M-1, int128]
  ComputeThread = TThread[int]
  Pair = tuple[value, weight: int32]

var
  leadingZeros: ZeroCounterLong
  lock: TLock
  innerProductTable: array[0..FMax, int8]
  zeroInnerProductList = newSeq[int32]()
  equiv: array[0..FMax, int32]
  fTable = newSeq[Pair]()

proc initInnerProductTables =
  for i in 0..FMax:
    innerProductTable[i] = int8(countBits32(int32(i)) - N div 2)
    if innerProductTable[i] == 0:
      if (i and 1) == 0:
        add(zeroInnerProductList, int32(i))

initInnerProductTables()

proc ror1(x: int): int {.inline.} =
  ((x shr 1) or (x shl (N-1))) and FMax

proc initEquivClasses =
  add(fTable, (0'i32, 1'i32))
  for i in 1..FMax:
    var r = i
    var found = false
    block loop:
      for j in 0..N-1:
        for m in [0, FMax]:
          if equiv[r xor m] != 0:
            fTable[equiv[r xor m]-1].weight += 1
            found = true
            break loop
        r = ror1(r)
    if not found:
      equiv[i] = int32(len(fTable)+1)
      add(fTable, (int32(i), 1'i32))

initEquivClasses()

when defined(gcc):
  const unrollDepth = 4
else:
  const unrollDepth = 4

proc search2(lz: var ZeroCounter, s0, f, w: int) =
  var s = s0
  for i in unrollDepth..M-1:
    lz[i] = lz[i] + uint64(w)
    s = s shr 1
    case innerProductTable[s xor f]
    of 0:
      # s = s + 0
    of -1:
      s = s + SStep
    else:
      return

template search(lz: var ZeroCounter, s, f, w, i: int) =
  when i < unrollDepth:
    lz[i] = lz[i] + uint64(w)
    if i < M-1:
      let s2 = s shr 1
      case innerProductTable[s2 xor f]
      of 0:
        search(lz, s2 + 0, f, w, i+1)
      of -1:
        search(lz, s2 + SStep, f, w, i+1)
      else:
        discard
  else:
    search2(lz, s, f, w)

proc worker(base: int) {.thread.} =
  var lz: ZeroCounter
  for fi in countup(base, len(fTable)-1, numThreads):
    let (fp, w) = fTable[fi]
    let f = if (fp and (FSize div 2)) == 0: fp else: fp xor FMax
    for sp in zeroInnerProductList:
      let s = f xor sp
      search(lz, s, f, w, 0)
  acquire(lock)
  for i in 0..M-1:
    let t = lz[i].toInt128 shl (M-i).toInt128
    leadingZeros[i] = leadingZeros[i] + t
  release(lock)

proc main =
  var threads: array[numThreads, ComputeThread]
  for i in 0 .. numThreads-1:
    createThread(threads[i], worker, i)
  for i in 0 .. numThreads-1:
    joinThread(threads[i])

initLock(lock)
main()
echo(@leadingZeros)

Il risultato con N = 22 è 12410090985684467712 che richiede 63,42 bit e quindi si inserisce in un 64-bit senza segno.
Stefan,

2
Hai decisamente alzato l'asticella in modo molto impressionante.

1
È bello vedere qualcuno che usa Nimrod. :)
cjfaure,

@Stefan Forse la tua procedura guidata di codifica potrebbe ottenere questo metodo sotto i 10 minuti per N = 22?

Ho provato N = 22 che è terminato dopo alcune ore. Tuttavia mi dà [-6036653088025083904, 2087229562810269696, 351.473.149.499,408384 millions, 59178309967151104, 9975110458933248, 1682628717576192, 284.866.824.372.224, 48.558.946,38592 milioni, 8.416.739,196928 millions, 1.518.499,004416 millions, 301.448.822.784, 71.620.493,312 mila, 22.100.246,528 mila, 8.676.573,184 mila, 3.897.278,464 mila, 1.860.960,256 mila, 911.646.720, 451.520.512, 224.785.920, 112.198.656, 56.062.720, 28.031.360, 14015680] che sembra essere un errore di overflow. Non conosco nimrod, ma è possibile utilizzare ints senza segno per risolvere questo problema?

11

Java ( n=22?)

Penso che la maggior parte delle risposte facciano meglio che n=16usare un approccio simile a questo, sebbene differiscano per le simmetrie che sfruttano e il modo in cui dividono il compito tra i thread.

I vettori definiti nella domanda possono essere sostituiti con stringhe di bit e il prodotto interno con XORing la finestra sovrapposta e verificando che siano n/2impostati esattamente i bit (e quindi i n/2bit cancellati). Ci sono n! / ((n/2)!)stringhe (coefficiente binomiale centrale) di nbit con n/2bit impostati (che chiamo stringhe bilanciate ), quindi per ogni dato Fci sono molte finestre di Scui danno un prodotto interno zero. Inoltre, l'azione di scorrere Slungo uno e verificare se è ancora possibile trovare un bit in entrata che fornisce un prodotto interno pari a zero corrisponde alla ricerca di un bordo in un grafico i cui nodi sono le finestre e i cui bordi collegano un nodo ua un nodo i vcui primi n-1bit sono gli ultimin-1pezzetti di u.

Ad esempio, con n=6e F=001001otteniamo questo grafico:

Grafico per F = 001001

e per F=001011questo otteniamo questo grafico:

Grafico per F = 001011

Poi abbiamo bisogno di contare per ogni ida 0a nquanti percorsi di lunghezza ici sono, sommando sui grafici per ogni F. Penso che molti di noi stiano usando la ricerca approfondita.

Si noti che i grafici sono sparsi: è facile dimostrare che ogni nodo ha un grado massimo di 1 e massimo di uno massimo. Ciò significa anche che le uniche strutture possibili sono catene semplici e semplici anelli. Questo semplifica un po 'il DFS.

Sfrutto un paio di simmetrie: le stringhe bilanciate vengono chiuse in bit inverse ( ~operazione in molte lingue della famiglia ALGOL) e in rotazione dei bit, in modo da poter raggruppare i valori Fcorrelati da queste operazioni e fare solo il DFS una volta.

public class CodeGolf26459v8D implements Runnable {
    private static final int NUM_THREADS = 8;

    public static void main(String[] args) {
        v8D(22);
    }

    private static void v8D(int n) {
        int[] bk = new int[1 << n];
        int off = 0;
        for (int i = 0; i < bk.length; i++) {
            bk[i] = Integer.bitCount(i) == n/2 ? off++ : -1;
        }

        int[] fwd = new int[off];
        for (int i = 0; i < bk.length; i++) {
            if (bk[i] >= 0) fwd[bk[i]] = i;
        }

        CodeGolf26459v8D[] runners = new CodeGolf26459v8D[NUM_THREADS];
        Thread[] threads = new Thread[runners.length];
        for (int i = 0; i < runners.length; i++) {
            runners[i] = new CodeGolf26459v8D(n, i, runners.length, bk, fwd);
            threads[i] = new Thread(runners[i]);
            threads[i].start();
        }

        try {
            for (int i = 0; i < threads.length; i++) threads[i].join();
        }
        catch (InterruptedException ie) {
            throw new RuntimeException("This shouldn't be reachable", ie);
        }

        long surviving = ((long)fwd.length) << (n - 1);
        for (int i = 0; i <= n; i++) {
            for (CodeGolf26459v8D runner : runners) surviving -= runner.survival[i];
            System.out.print(i == 0 ? "[" : ", ");
            java.math.BigInteger result = new java.math.BigInteger(Long.toString(surviving));
            System.out.print(result.shiftLeft(n + 1 - i));
        }
        System.out.println("]");
    }

    public final int n;
    protected final int id;
    protected final int numRunners;
    private final int[] bk;
    private final int[] fwd;

    public long[] survival;

    public CodeGolf26459v8D(int n, int id, int numRunners, int[] bk, int[] fwd) {
        this.n = n;
        this.id = id;
        this.numRunners = numRunners;

        this.bk = bk;
        this.fwd = fwd;
    }

    private int dfs2(int[] graphShape, int flip, int i) {
        if (graphShape[i] != 0) return graphShape[i];

        int succ = flip ^ (fwd[i] << 1);
        if (succ >= bk.length) succ ^= bk.length + 1;

        int j = bk[succ];
        if (j == -1) return graphShape[i] = 1;

        graphShape[i] = n + 1; // To detect cycles
        return graphShape[i] = dfs2(graphShape, flip, j) + 1;
    }

    @Override
    public void run() {
        int n = this.n;
        int[] bk = this.bk;
        int[] fwd = this.fwd;

        // NB The initial count is approx 2^(2n - 1.33 - 0.5 lg n)
        // For n=18 we overflow 32-bit
        // 64-bit is good up to n=32.
        long[] survival = new long[n + 1];
        boolean[] visited = new boolean[1 << (n - 1)];
        int th = 0;
        for (int f = 0; f < visited.length; f++) {
            if (visited[f]) continue;

            int m = 1, g = f;
            while (true) {
                visited[g] = true;
                int ng = g << 1;
                if ((ng >> (n - 1)) != 0) ng ^= (1 << n) - 1;
                if (ng == f) break;
                m++;
                g = ng;
            }

            if (th++ % numRunners != id) continue;

            int[] graphShape = new int[fwd.length];
            int flip = (f << 1) ^ f;
            for (int i = 0; i < graphShape.length; i++) {
                int life = dfs2(graphShape, flip, i);
                if (life <= n) survival[life] += m;
            }
        }

        this.survival = survival;
    }
}

Sul mio Core 2 a 2,5 GHz ottengo

# n=18
$ javac CodeGolf26459v8D.java && time java CodeGolf26459v8D
[3341140958904320, 619683355033600, 115151552380928, 21392898654208, 3982886961152, 744128512000, 141108051968, 27588886528, 5800263680, 1408761856, 438001664, 174358528, 78848000, 38050816, 18762752, 9346816, 4666496, 2333248, 1166624]

real    0m3.131s
user    0m10.133s
sys     0m0.380s

# n=20
$ javac CodeGolf26459v8D.java && time java CodeGolf26459v8D
[203141370301382656, 35792910586740736, 6316057966936064, 1114358247587840, 196906665902080, 34848574013440, 6211866460160, 1125329141760, 213330821120, 44175523840, 11014471680, 3520839680, 1431592960, 655872000, 317675520, 156820480, 78077440, 39005440, 19501440, 9750080, 4875040]

real    1m8.706s
user    4m20.980s
sys     0m0.564s

# n=22
$ javac CodeGolf26459v8D.java && time java CodeGolf26459v8D
[12410090985684467712, 2087229562810269696, 351473149499408384, 59178309967151104, 9975110458933248, 1682628717576192, 284866824372224, 48558946385920, 8416739196928, 1518499004416, 301448822784, 71620493312, 22100246528, 8676573184, 3897278464, 1860960256, 911646720, 451520512, 224785920, 112198656, 56062720, 28031360, 14015680]

real    20m10.654s
user    76m53.880s
sys     0m6.852s

Poiché il computer di Lembik ha 8 core ed ha eseguito il mio precedente programma a thread singolo due volte più veloce del mio, sono ottimista sul fatto che verrà eseguito n=22in meno di 8 minuti.


07:17! Molto bella. Ti dispiacerebbe spiegare un po 'di più il tuo metodo?

6

C

Fondamentalmente è solo un'implementazione leggermente ottimizzata dell'algoritmo nella domanda. Può gestire n=12entro il limite di tempo.

#include <stdio.h>
#include <inttypes.h>

#define n 12
#define m (n + 1)

int main() {
    int i;
    uint64_t S, F, o[m] = {0};
    for (S = 0; S < (1LLU << (n + m - 1)); S += 2)
        for (F = 0; F < (1 << (n - 1)); F++)
            for (i = 0; i < m; i++)
                if (__builtin_popcount(((S >> i) & ((1 << n) - 1)) ^ F) == n >> 1)
                    o[i] += 4;
                else
                    break;
    for (i = 0; i < m; i++)
        printf("%" PRIu64 " ", o[i]);
    return 0;
}

Test eseguito n=12, inclusa la compilazione:

$ clang -O3 -march=native -fstrict-aliasing -ftree-vectorize -Wall fast.c
$ time ./a.out 
15502147584 3497066496 792854528 179535872 41181184 9826304 2603008 883712 381952 177920 85504 42560 21280 
real    0m53.266s
user    0m53.042s
sys     0m0.068s
$

Commento: ho appena acceso il cervello e ho usato alcune semplici combinazioni per calcolare che il primo valore sarà sempre n! / ((n / 2)!)^2 * 2^(n + m - 1). Mi sembra che ci debba essere una soluzione completamente algebrica a questo problema.


Ricevo molti avvisi quando compilo questo. Prova gcc -Wall -Wextra Fors.c -o Fors

C'erano un paio di variabili inutilizzate dimenticate da una precedente iterazione, ma le ho rimosse così almeno un paio di avvertimenti avrebbero dovuto scomparire. Al momento non ho GCC disponibile (solo Clang) e Clang non mi dà alcun avviso al momento (dopo aver rimosso le variabili non utilizzate). E dato che Clang di solito è più severo quando si tratta di avvertimenti, devo dire che sono un po 'sorpreso che tu abbia ricevuto qualche avvertimento.
Fors,

Si lamenta di Fors.c: 13: 17: avvertimento: suggerisci parentesi attorno a '-' nell'operando di '&' [-Wententeses] (due volte) e anche avvertimento: il formato '% llu' prevede argomento di tipo 'lungo lungo non firmato int ', ma l'argomento 2 ha il tipo' uint64_t '[-Wformat =]. In effetti clang si lamenta anche della dichiarazione printf per me,

Con le ultime modifiche, GCC non dovrebbe generare alcun messaggio di avviso.
Fors,

Si lamenta ancora per Fors.c: 13: 49: avvertimento: suggerire parentesi attorno all'aritmetica nell'operando di '^' [-Parentesi] Ma in peggiori notizie ... ci vogliono più di 10 minuti sulla mia macchina.

5

Giava, n=16

Per ogni dato valore di Fci sono \binom{n}{n/2}vettori che hanno un prodotto interno zero con esso. Quindi possiamo costruire un grafico i cui vertici sono quei vettori corrispondenti e i cui bordi corrispondono allo spostamento di S, e quindi dobbiamo solo contare i percorsi di lunghezza fino a nnel grafico.

Non ho provato a microottimizzare questo sostituendo i condizionali con operazioni bit per bit, ma ogni doppio incremento naumenta il tempo di esecuzione di circa 16 volte, quindi non farà abbastanza differenza a meno che non sia abbastanza vicino alla soglia. Sulla mia macchina, non lo sono.

public class CodeGolf26459 {

    public static void main(String[] args) {
        v3(16);
    }

    // Order of 2^(2n-1) * n ops
    private static void v3(int n) {
        long[] counts = new long[n+1];
        int mask = (1 << n) - 1;
        for (int f = 0; f < (1 << (n-1)); f++) {
            // Find adjacencies
            long[] subcounts = new long[1 << n];
            for (int g = 0; g < (1 << n); g++) {
                subcounts[g] = Integer.bitCount(f ^ g) == n/2 ? 2 : -1;
            }

            for (int round = 0; round <= n; round++) {
                long count = 0;
                // Extend one bit.
                long[] next = new long[1 << n];
                for (int i = 0; i < (1 << n); i++) {
                    long s = subcounts[i];
                    if (s == -1) next[i] = -1;
                    else {
                        count += s;
                        int j = (i << 1) & mask;
                        if (subcounts[j] >= 0) next[j] += s;
                        if (subcounts[j + 1] >= 0) next[j + 1] += s;
                    }
                }
                counts[round] += count << (n - round);
                subcounts = next;
            }
        }

        System.out.print("[");
        for (long count : counts) System.out.print(count+", ");
        System.out.println("]");
    }
}

Sul mio Core 2 a 2,5 GHz ottengo

$ javac CodeGolf26459.java && time java -server CodeGolf26459 
[55276229099520, 10855179878400, 2137070108672, 420578918400, 83074121728, 16540581888, 3394347008, 739659776, 183838720, 57447424, 23398912, 10749184, 5223040, 2584896, 1291424, 645200, 322600, ]

real    6m2.663s
user    6m4.631s
sys     0m1.580s

Piggybacking poiché non voglio implementare la mia soluzione proprio ora. Ogni vertice ha al massimo un successore, quindi non hai davvero bisogno dell'array. Per iterare in modo efficiente su combinazioni di fvertici iniziali e, iterare su tutto f_xor_gcon n/2bit impostati esattamente . Per ognuno di questi, ripeti su tutto fe prendi g = f ^ f_xor_g.
David Eisenstat,

@David, lo so, e la mia versione 7 fa n = 18 in un minuto sul mio netbook Atom, ma non posso pubblicarlo fino a quando non torno dalle vacanze.
Peter Taylor,

4

RPython, N = 22 ~ 3: 23

Multi-thread, utilizzando una discesa ricorsiva impilabile. Il programma accetta due argomenti della riga di comando: N e il numero di thread di lavoro.

from time import sleep

from rpython.rlib.rthread import start_new_thread, allocate_lock
from rpython.rlib.rarithmetic import r_int64, build_int, widen
from rpython.rlib.rbigint import rbigint

r_int8 = build_int('r_char', True, 8)

class ThreadEnv:
  __slots__ = ['n', 'counts', 'num_threads',
               'v_range', 'v_num', 'running', 'lock']

  def __init__(self):
    self.n = 0
    self.counts = [rbigint.fromint(0)]
    self.num_threads = 0
    self.v_range = [0]
    self.v_num = 0
    self.running = 0
    self.lock = None

env = ThreadEnv()

bt_bits = 12
bt_mask = (1<<bt_bits)-1
# computed compile time
bit_table = [r_int8(0)]
for i in xrange(1,1<<bt_bits):
  bit_table += [((i&1)<<1) + bit_table[i>>1]]

def main(argv):
  argc = len(argv)
  if argc < 2 or argc > 3:
    print 'Usage: %s N [NUM_THREADS=2]'%argv[0]
    return 1

  if argc == 3:
    env.num_threads = int(argv[2])
  else:
    env.num_threads = 2

  env.n = int(argv[1])
  env.counts = [rbigint.fromint(0)]*env.n
  env.lock = allocate_lock()

  v_range = []
  v_max = 1<<(env.n-1)
  v_num = 0
  v = (1<<(env.n>>1))-1
  while v < v_max:
    v_num += 1
    v_range += [v]
    if v&1:
      # special case odd v
      s = (v+1)&-v
      v ^= s|(s>>1)
    else:
      s = v&-v
      r = v+s
      # s is at least 2, skip two iterations
      i = 3
      s >>= 2
      while s:
        i += 1
        s >>= 1
      v = r|((v^r)>>i)
  env.v_range = v_range
  env.v_num = v_num

  for i in xrange(env.num_threads-1):
    start_new_thread(run,())

  # use the main process as a worker
  run()

  # wait for any laggers
  while env.running:
    sleep(0.05)

  result = []
  for i in range(env.n):
    result += [env.counts[i].lshift(env.n-i+3).str()]
  result += [env.counts[env.n-1].lshift(3).str()]
  print result
  return 0

def run():
  with env.lock:
    v_start = env.running
    env.running += 1

  n = env.n
  counts = [r_int64(0)]*n
  mask = (1<<n)-1
  v_range = env.v_range
  v_num = env.v_num
  z_count = 1<<(n-2)

  for i in xrange(v_start, v_num, env.num_threads):
    v = v_range[i]
    counts[0] += z_count
    counts[1] += v_num
    r = v^(v<<1)
    for w in v_range:
      # unroll counts[2] for speed
      # ideally, we could loop over x directly,
      # rather than over all v, only to throw the majority away
      # there's a 2x-3x speed improvement to be had here...
      x = w^r
      if widen(bit_table[x>>bt_bits]) + widen(bit_table[x&bt_mask]) == n:
        counts[2] += 1
        x, y = v, x
        o, k = 2, 3
        while k < n:
          # x = F ^ S
          # y = F ^ (S<<1)
          o = k
          z = (((x^y)<<1)^y)&mask
          # z is now F ^ (S<<2), possibly xor 1
          # what S and F actually are is of no consequence

          # the compiler hint `widen` let's the translator know
          # to store the result as a native int, rather than a signed char
          bt_high = widen(bit_table[z>>bt_bits])
          if bt_high + widen(bit_table[z&bt_mask]) == n:
            counts[k] += 1
            x, y = y, z
            k += 1
          elif bt_high + widen(bit_table[(z^1)&bt_mask]) == n:
            counts[k] += 1
            x, y = y, z^1
            k += 1
          else: k = n

  with env.lock:
    for i in xrange(n):
      env.counts[i] = env.counts[i].add(rbigint.fromrarith_int(counts[i]))
    env.running -= 1

def target(*args):
  return main, None

Compilare

Crea un clone locale del repository PyPy usando mercurial, git o qualunque cosa tu preferisca. Digita il seguente incantesimo (supponendo che lo script sopra sia chiamato convolution-high.py):

$ pypy %PYPY_REPO%/rpython/bin/rpython --thread convolution-high.py

dove %PYPY_REPO%rappresenta una variabile di ambiente che punta al repository appena clonato. La compilazione richiede circa un minuto.


Tempi di campionamento

N = 16, 4 fili:

$ timeit convolution-high-c 16 4
[55276229099520, 10855179878400, 2137070108672, 420578918400, 83074121728, 16540581888, 3394347008, 739659776, 183838720, 57447424, 23398912, 10749184, 5223040, 2584896, 1291424, 645200, 322600]
Elapsed Time:     0:00:00.109
Process Time:     0:00:00.390

N = 18, 4 fili:

$ timeit convolution-high-c 18 4
[3341140958904320, 619683355033600, 115151552380928, 21392898654208, 3982886961152, 744128512000, 141108051968, 27588886528, 5800263680, 1408761856, 438001664, 174358528, 78848000, 38050816, 18762752, 9346816, 4666496, 2333248, 1166624]
Elapsed Time:     0:00:01.250
Process Time:     0:00:04.937

N = 20, 4 fili:

$ timeit convolution-high-c 20 4
[203141370301382656, 35792910586740736, 6316057966936064, 1114358247587840, 196906665902080, 34848574013440, 6211866460160, 1125329141760, 213330821120, 44175523840, 11014471680, 3520839680, 1431592960, 655872000, 317675520, 156820480, 78077440, 39005440, 19501440, 9750080, 4875040]
Elapsed Time:     0:00:15.531
Process Time:     0:01:01.328

N = 22, 4 fili:

$ timeit convolution-high-c 22 4
[12410090985684467712, 2087229562810269696, 351473149499408384, 59178309967151104, 9975110458933248, 1682628717576192, 284866824372224, 48558946385920, 8416739196928, 1518499004416, 301448822784, 71620493312, 22100246528, 8676573184, 3897278464, 1860960256, 911646720, 451520512, 224785920, 112198656, 56062720, 28031360, 14015680]
Elapsed Time:     0:03:23.156
Process Time:     0:13:25.437

09:26. Benvenuto nell'equipaggio 22 :)

Non sono sicuro del perché, ma la tua nuova versione non è più veloce per me. Ancora verso le 9:30 quando faccio il tempo ./primo-c 22 8.

@Lembik avrebbe senso se la divisione fosse mediamente più veloce di 3 turni a destra (3 = Somma {(n + 1) / (2 ^ n)}, n = 1..infty). Per le architetture certiane, suppongo che potrebbe essere il caso, ma sulla mia divisione è notevolmente più lenta. Grazie per aver dedicato del tempo a testarlo :)
primo

3

Python 3.3, N = 20, 3.5min

Dichiarazione di non responsabilità: la mia intenzione NON è di pubblicare questo come una mia risposta, poiché l'algoritmo che sto usando è solo una porta spudorata della soluzione RPython di Primo . Il mio scopo qui è solo quello di mostrare cosa puoi fare in Python se combini la magia dei moduli Numpy e Numba .

Numba ha spiegato in breve:

Numba è un compilatore specializzato just-in-time che compila il codice Python e NumPy annotato in LLVM (tramite decoratori). http://numba.pydata.org/

Aggiornamento 1 : Ho notato dopo aver lanciato i numeri che possiamo semplicemente saltare completamente alcuni dei numeri. Quindi ora maxf diventa (1 << n) // 2 e maxs diventa maxf 2 **. Questo accelererà un po 'il processo. n = 16 ora richiede solo ~ 48 secondi (invece che di 4,5 minuti). Ho anche un'altra idea che proverò a vedere se riesco a farlo andare un po 'più veloce.

Aggiornamento 2: algoritmo modificato (soluzione di primo). Sebbene la mia porta non supporti ancora il multithreading, è piuttosto banale aggiungere. È anche possibile rilasciare CPython GIL usando Numba e ctypes. Questa soluzione, tuttavia, funziona molto velocemente anche su single core!

import numpy as np
import numba as nb

bt_bits = 11
bt_mask = (1 << bt_bits) - 1
bit_table = np.zeros(1 << bt_bits, np.int32)

for i in range(0, 1 << bt_bits):
    bit_table[i] = ((i & 1) << 1) + bit_table[i >> 1]

@nb.njit("void(int32, int32, int32, int32, int64[:], int64[:])")
def run(n, m, start, re, counts, result):
    mask = (1 << n) - 1

    v_max = (1 << n) // 2
    rr = v_max // 2

    v = (1 << (n >> 1)) - 1
    while v < v_max:
        s = start

        while s < rr:
            f = v ^ s
            counts[0] += 8
            t = s << 1
            o, j = 0, 1

            while o < j and j < m:
                o = j
                w = (t ^ f) & mask
                bt_high = bit_table[w >> bt_bits]

                if bt_high + bit_table[w & bt_mask] == n:
                    counts[j] += 8
                    t <<= 1
                    j += 1
                elif bt_high + bit_table[(w ^ 1) & bt_mask] == n:
                    counts[j] += 8
                    t = (t | 1) << 1
                    j += 1
                    s += re

            s = v & -v
            r = v + s
            o = v ^ r
            o = (o >> 2) // s
            v = r | o

    for e in range(m):
        result[e] += counts[e] << (n - e)

E infine:

if __name__ == "__main__":
    n = 20
    m = n + 1

    result = np.zeros(m, np.int64)
    counts = np.zeros(m, np.int64)

    s1 = time.time() * 1000
    run(n, m, 0, 1, counts, result)
    s2 = time.time() * 1000

    print(result)
    print("{0}ms".format(s2 - s1))

Funziona sulla mia macchina in 212688ms o ~ 3.5min.


Grazie. Che ne dici di n = 18? :)

Sono passati quasi 20 minuti da quando ho avviato il programma usando n = 18. Penso che sia sicuro dire che Python non può risolverlo nemmeno con Numba in tempo usando questo particolare algoritmo.
Anna Jokela,

Sono ottimista che esista un algoritmo migliore.

Ho provato a installare pip numba ma dice che non riesce a trovare llvmpy. Ho provato sudo pip install llvmpy ma mi dice che non riesce a trovare Versioneer. Ho provato sudo pip install versioneer ma dice che ce l'ho già.

Anche se non ho ancora numba per funzionare (penso che dovrò installare anaconda alla fine), sono rimasto colpito da questo. La domanda è: riesci a risolverlo usando N = 22 usando un metodo simile a quello di nimrod?

2

C ++ N = 16

Sto testando un EEEPC con un atomo .. il mio tempo non ha molto senso. : D
L'atomo risolve n = 14 in 34 secondi. E n = 16 in 20 minuti. Voglio testare n = 16 su OP pc. Sono ottimista.

L'idea è che ogni volta che troviamo una soluzione per una data F abbiamo trovato la soluzione 2 ^ i perché possiamo cambiare la parte inferiore di S portando allo stesso risultato.

#include <stdio.h>
#include <cinttypes>
#include <cstring>

int main()
{
   const int n = 16;
   const int m = n + 1;
   const uint64_t maxS = 1ULL << (2*n);
   const uint64_t maxF = 1ULL << n;
   const uint64_t mask = (1ULL << n)-1;
   uint64_t out[m]={0};
   uint64_t temp[m] = {0};
   for( uint64_t F = 0; F < maxF; ++F )
   {
      for( uint64_t S = 0; S < maxS; ++S )
      {
         int numSolution = 1;
         for( int i = n; i >= 0; --i )
         {
            const uint64_t window = S >> i;
            if( __builtin_popcount( mask & (window ^ F) ) == (n / 2) )
            {
               temp[i] += 1;
            } else {
               numSolution = 1 << i;
               S += numSolution - 1;
               break;
            }
         }
         for( int i = n; i >= 0; --i )
         {
            out[i] += temp[i]*numSolution;
            temp[i] = 0;
         }
      }
   }
   for( int i = n; i >= 0; --i )
   {
      uint64_t x = out[i];
      printf( "%lu ", x );
   }
   return 0;
}

compilare:

gcc 26459.cpp -std = c ++ 11 -O3 -march = native -fstrict-aliasing -ftree-vectorize -Wall -pedantic -o 26459


1
Questo è fantastico Ho alcune idee cotte in realtà su come risolverlo per n più grandi. Ti piacerebbe ascoltarli o questo potrebbe rovinare la concorrenza?

2

JAVASCRIPT n: 12

Nel mio computer ci sono voluti 231.242 secondi. Nella demo sto usando i webworker per impedire il blocco del browser. Questo sicuramente può essere ulteriormente migliorato con i lavoratori paralleli. So che JS non ha alcuna possibilità in questa sfida, ma l'ho fatto per divertimento!

Fare clic per eseguire la demo online

var n = 8;        
var m = n + 1;
var o = [];
var popCount = function(bits) {
  var SK5  = 0x55555555,
      SK3  = 0x33333333,
      SKF0 = 0x0f0f0f0f,
      SKFF = 0xff00ff;

  bits -= (bits >> 1) & SK5;
  bits  = (bits & SK3) + ((bits >> 2) & SK3);
  bits  = (bits & SKF0) + ((bits >> 4) & SKF0);
  bits += bits >> 8;

  return (bits + (bits >> 15)) & 63;
};
for(var S = 0; S < (1 << n + m - 1); S += 2){
  for(var F = 0; F < (1 << n - 1); F += 1){
    for (var i = 0; i < m; i++){
      var c = popCount(((S >> i) & ((1 << n) - 1)) ^ F);
      if(c == n >> 1){
        if(!o[i]) o[i] = 0;
        o[i] += 4;
      } else break;
    }
  }
}
return o;

Che dire di uno di quei nuovi (veloci) motori javascript? Potrebbero essere usati?

Intendi qualcosa come il dardo ?
rafaelcastrocouto,

1
In realtà mi sbaglio. Potresti anche provare sia Firefox che il cromo. A meno che tu non voglia scriverlo in asm.js ovviamente :)

1
sfida accettata ... lo farò!
rafaelcastrocouto,

1
Ho provato questo e il mio computer ha impiegato 5,4 secondi per fare n=22 [235388928,86292480,19031048,5020640,1657928,783920,545408,481256,463832,460256,459744,459744,459744,459744,459744,459744,459744,459744,459744,459744,459744,459744] i.imgur.com/FIJa2Ch.png
Spedwards

1

Fortran: n = 12

Ho appena realizzato una versione rapida e trenta in Fortran, nessuna ottimizzazione tranne OpenMP. Dovrebbe spremere appena sotto i 10 minuti per n = 12 sulla macchina OP, ci vogliono 10:39 sulla mia macchina che è leggermente più lenta.

Gli interi a 64 bit hanno effettivamente un impatto negativo sulle prestazioni; Immagino che dovrei ripensare l'intero algoritmo affinché questo sia molto più veloce. Non so se mi preoccuperò, penso che passerò un po 'di tempo a pensare a una buona sfida che è più per i miei gusti. Se qualcun altro vuole prendere questo e correre con esso, vai avanti :)

program golf
use iso_fortran_env
implicit none
integer, parameter ::  n=12
integer :: F(n), S(2*n)
integer(int64) :: leadingzerocounts(n+1)
integer :: k
integer(int64) :: i,j,bindec,enc

leadingzerocounts=0

!$OMP parallel do private(i,enc,j,bindec,S,F,k) reduction(+:leadingzerocounts) schedule(dynamic)
do i=0,2**(2*n)-1
  enc=i
  ! Short loop to convert i into the array S with -1s and 1s
  do j=2*n,1,-1
    bindec=2**(j-1)
    if (enc-bindec .ge. 0) then
      S(j)=1
      enc=enc-bindec
    else
      S(j)=-1
    endif
  end do
  do j=0,2**(n)-1
    ! Convert j into the array F with -1s and 1s
    enc=j
    do k=n,1,-1
      bindec=2**(k-1)
      if (enc-bindec .ge. 0) then
        F(k)=1
        enc=enc-bindec
      else
        F(k)=-1
      endif
    end do
    ! Compute dot product   
    do k=1,n+1
      if (dot_product(F,S(k:k+n-1)) /= 0) exit
      leadingzerocounts(k)=leadingzerocounts(k)+1
    end do
  end do
end do
!$OMP end parallel do

print *, leadingzerocounts

end

1

Lua: n = 16

Disclaimer: la mia intenzione NON è quella di pubblicare questo come una mia risposta, poiché l'algoritmo che sto usando è sottratto senza vergogna alla risposta intelligente di Anna Jokela . che fu spudoratamente rubato dalla risposta intelligente di Ilmale .

Inoltre, non è nemmeno valido - ha inesattezze causate da numeri in virgola mobile (sarebbe meglio se Lua supportasse numeri interi a 64 bit). Tuttavia, lo sto ancora caricando, solo per mostrare quanto è veloce questa soluzione. È un linguaggio di programmazione dinamico, eppure posso calcolare n = 16 in un tempo ragionevole (1 minuto su CPU a 800 MHz).

Funziona con LuaJIT, l'interprete standard è troppo lento.

local bit = require "bit"
local band = bit.band
local bor = bit.bor
local bxor = bit.bxor
local lshift = bit.lshift
local rshift = bit.rshift

-- http://stackoverflow.com/a/11283689/736054
local function pop_count(w)
    local b1 = 1431655765
    local b2 = 858993459
    local b3 = 252645135
    local b7 = 63

    w = band(rshift(w, 1), b1) + band(w, b1)
    w = band(rshift(w, 2), b2) + band(w, b2)
    w = band(w + rshift(w, 4), b3)
    return band(rshift(w, 24) + rshift(w, 16) + rshift(w, 8) + w, b7)
end

local function gen_array(n, value)
    value = value or 0
    array = {}
    for i = 1, n do
        array[i] = value
    end
    return array
end

local n = 16
local u = math.floor(n / 2)
local m = n + 1
local maxf = math.floor(lshift(1, n) / 2)
local maxs = maxf ^ 2
local mask = lshift(1, n) - 1

local out = gen_array(m, 0)
local temp = gen_array(m, 0)


for f = 0, maxf do
    local s = 0
    while s <= maxs do
        local num_solution = 1

        for i = n, 0, -1 do
            if pop_count(band(mask, bxor(rshift(s, i), f))) == u then
                temp[i + 1] = temp[i + 1] + 8
            else
                num_solution = lshift(1, i)
                s = s + num_solution - 1
                break
            end
        end

        for i = 1, m do
            out[i] = out[i] + temp[i] * num_solution
            temp[i] = 0
        end

        s = s + 1
    end
end

for i = m, 1, -1 do
    print(out[i])
end

Grazie. Penso che le recenti versioni di lua utilizzino int long long che dovrebbe essere a 64 bit su un sistema a 64 bit. Vedi "lua_integer" su lua.org/work/doc/manual.html .

@Lembik: interessante. Ad ogni modo, è Lua standard (che supporta già long longanziché doublecon un'impostazione di compilazione), non LuaJIT.
Konrad Borowski

Penso che mi sbagliassi in ogni caso con Luajit. Uno avrebbe bisogno di 5.3 che non esiste. Il miglior consiglio che le persone di lua potevano dare era "provare 5.3-workx".
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.