Contare le matrici di periodi


11

Il perioddi una stringa è lo spostamento diverso da zero più breve in modo che la stringa corrisponda a se stessa, ignorando tutte le parti che sporgono. Quindi, ad esempio, abcabcabha periodo 3. Per convenzione diciamo che se non esiste tale spostamento, una stringa ha un periodo pari alla sua lunghezza. Quindi il periodo di abcdeè 5e il periodo di aè 1.

In termini più formali, il periodo di una stringa Sè il minimo, i > 0quindi S[1,n-i] == S[i+1,n](indicizzazione da 1).

Per una data stringa S di potenza di due lunghezze, calcoleremo il periodo di tutti i suoi prefissi di potenza di due lunghezze. Ad esempio, considera S = abcabcab. I periodi che calcoleremo sono:

'a', 1
'ab', 2
'abca', 3
'abcabcab', 3

In effetti, avremo solo l'output della matrice di punti, cioè [1, 2, 3, 3].

Per una data potenza positiva di due n, considera tutte le possibili stringhe binarie S. Ricordiamo che una stringa binaria è semplicemente una stringa di 1s e 0s così ci sono esattamente 2^ntali stringhe (che è 2alla potenza n). Per ognuno possiamo calcolare questa serie di periodi.

La sfida è scrivere un codice che accetta n(una potenza di due) come input e calcola quanti array così distinti ci sono.

Le risposte per n = 1, 2, 4, 8, 16, 32, 64, 128sono:

1, 2, 6, 32, 320, 6025, 216854, 15128807

L'insieme completo di matrici di periodi distinti per n = 4è:

1, 1, 1
1, 1, 3
1, 1, 4
1, 2, 2
1, 2, 3
1, 2, 4

Punto

Eseguirò il tuo codice sul mio computer con Ubuntu per 10 minuti. Il tuo punteggio è il più grande nper il quale il tuo codice termina in quel momento. In caso di pareggio, nvince la risposta che completa il più grande comune più veloce. Nel caso in cui ci sia un pareggio entro 1 secondo sugli orari, la prima risposta pubblicata vince.

Lingue e biblioteche

Puoi utilizzare qualsiasi lingua e libreria disponibili. Si prega di includere una spiegazione completa su come eseguire / compilare il codice in Linux, se possibile. »

Il tuo codice dovrebbe effettivamente calcolare le risposte e non, ad esempio, generare solo valori precalcolati.

Voci principali

  • 2 minuti e 21 secondi per n = 128 in C # di Peter Taylor
  • 9 secondi per n = 32 in Rust di isaacg

Questo mi ha fatto male alla testa.
Henry,

1
La sfida è interessante, ma non riesco ancora a vedere un criterio oggettivo che stai usando per distinguere tra risposte "pre - calcolate" e "effettivamente calcolate" . Se, ad esempio, non riesci a capire come funziona il mio codice, ma fornisce risposte esatte per enormi n, lo accetteresti? Non è ben definito dove si trova il confine tra hardcoding e calcolo effettivo.


1
@ThePirateBay codegolf.meta.stackexchange.com/a/1063/9206 . È una regola standard.

2
@Cowsquack Tutti tranne le prime tre lettere della stringa sono abcab. Sono tutte tranne le ultime 3 lettere abcab. Questi corrispondono e la rimozione di un numero inferiore di lettere non corrisponde.
isaacg,

Risposte:


9

C #, n = 128 in circa 2:40

using System;
using System.Collections.Generic;
using System.Linq;

namespace Sandbox
{
    class PPCG137436
    {
        public static void Main(string[] args)
        {
            if (args.Length == 0) args = new string[] { "1", "2", "4", "8", "16", "32", "64", "128" };

            foreach (string arg in args)
            {
                Console.WriteLine(Count(new int[(int)(0.5 + Math.Log(int.Parse(arg)) / Math.Log(2))], 0));
            }
        }

        static int Count(int[] periods, int idx)
        {
            if (idx == periods.Length)
            {
                //Console.WriteLine(string.Join(", ", periods));
                return 1;
            }

            int count = 0;
            int p = idx == 0 ? 1 : periods[idx - 1];
            for (int q = p; q <= 1 << (idx + 1); q++)
            {
                periods[idx] = q;
                if (q == p || q > 1 << idx || p + q - Gcd(p, q) > 1 << idx && UnificationPasses(periods, idx, q)) count += Count(periods, idx + 1);
            }

            return count;
        }

        private static int Gcd(int a, int b)
        {
            while (a > 0) { int tmp = a; a = b % a; b = tmp; }
            return b;
        }

        private static bool UnificationPasses(int[] periods, int idx, int q)
        {
            UnionSet union = new UnionSet(1 << idx);
            for (int i = 0; i <= idx; i++)
            {
                for (int j = 0; j + periods[i] < Math.Min(2 << i, 1 << idx); j++) union.Unify(j, j + periods[i]);
            }

            IDictionary<int, long> rev = new Dictionary<int, long>();
            for (int k = 0; k < 1 << idx; k++) rev[union.Find(k)] = 0L;
            for (int k = 0; k < 1 << idx; k++) rev[union.Find(k)] |= 1L << k;

            long zeroes = rev[union.Find(0)]; // wlog the value at position 0 is 0

            ISet<int> onesIndex = new HashSet<int>();

            // This can be seen as the special case of the next loop where j == -1.
            for (int i = 0; i < idx; i++)
            {
                if (periods[i] == 2 << i) onesIndex.Add((2 << i) - 1);
            }
            for (int j = 0; j < idx - 1 && periods[j] == 2 << j; j++)
            {
                for (int i = j + 1; i < idx; i++)
                {
                    if (periods[i] == 2 << i)
                    {
                        for (int k = (1 << j) + 1; k <= 2 << j; k++) onesIndex.Add((2 << i) - k);
                    }
                }
            }

            for (int i = 1; i < idx; i++)
            {
                if (periods[i] == 1) continue;

                int d = (2 << i) - periods[i];
                long dmask = (1L << d) - 1;
                if (((zeroes >> 1) & (zeroes >> periods[i]) & dmask) == dmask) onesIndex.Add(periods[i] - 1);
            }

            long ones = 0L;
            foreach (var key in onesIndex) ones |= rev[union.Find(key)];

            if ((zeroes & ones) != 0) return false; // Definite contradiction!

            rev.Remove(union.Find(0));
            foreach (var key in onesIndex) rev.Remove(key);

            long[] masks = System.Linq.Enumerable.ToArray(rev.Values);

            int numFilteredMasks = 0;
            long set = 0;
            long M = 0;
            for (int i = 1; i <= idx; i++)
            {
                if (periods[i - 1] == 1) continue;

                // Sort the relevant masks to the start
                if (i == idx) numFilteredMasks = masks.Length; // Minor optimisation: skip the filter because we know we need all the masks
                long filter = (1L << (1 << i)) - 1;
                for (int j = numFilteredMasks; j < masks.Length; j++)
                {
                    if ((masks[j] & filter) != 0)
                    {
                        var tmp = masks[j];
                        masks[j] = masks[numFilteredMasks];
                        masks[numFilteredMasks++] = tmp;
                    }
                }

                // Search for a successful assignment, using the information from the previous search to skip a few initial values in this one.
                set |= (1L << numFilteredMasks) - 1 - M;
                M = (1L << numFilteredMasks) - 1;
                while (true)
                {
                    if (TestAssignment(periods, i, ones, masks, set)) break;
                    if (set == 0) return false; // No suitable assignment found

                    // Gosper's hack with variant to reduce the number of bits on overflow
                    long c = set & -set;
                    long r = set + c;
                    set = (((r ^ set) >> 2) / c) | (r & M);
                }
            }

            return true;
        }

        private static bool TestAssignment(int[] periods, int idx, long ones, long[] masks, long assignment)
        {
            for (int j = 0; j < masks.Length; j++, assignment >>= 1) ones |= masks[j] & -(assignment & 1);
            for (int i = idx - 1; i > 0; i--) // i == 0 is already handled in the unification process.
            {
                if (Period(ones, 2 << i, periods[i - 1]) < periods[i]) return false;
            }

            return true;
        }

        private static int Period(long arr, int n, int min)
        {
            for (int p = min; p <= n; p++)
            {
                // If the bottom n bits have period p then the bottom (n-p) bits equal the bottom (n-p) bits of the integer shifted right p
                long mask = (1L << (n - p)) - 1L;
                if ((arr & mask) == ((arr >> p) & mask)) return p;
            }

            throw new Exception("Unreachable");
        }

        class UnionSet
        {
            private int[] _Lookup;

            public UnionSet(int size)
            {
                _Lookup = new int[size];
                for (int k = 0; k < size; k++) _Lookup[k] = k;
            }

            public int Find(int key)
            {
                var l = _Lookup[key];
                if (l != key) _Lookup[key] = l = Find(l);
                return l;
            }

            public void Unify(int key1, int key2)
            {
                int root1 = Find(key1);
                int root2 = Find(key2);

                if (root1 < root2) _Lookup[root2] = root1;
                else _Lookup[root1] = root2;
            }
        }
    }
}

L'estensione a n = 256 richiederebbe il passaggio a BigIntegerper le maschere, che probabilmente uccide troppo le prestazioni perché n = 128 passi senza nuove idee, figuriamoci n = 256.

Sotto Linux, compilare mono-csced eseguire con mono.

Spiegazione di base

Non ho intenzione di fare una dissezione riga per riga, ma solo una panoramica dei concetti.

Come regola generale, sono felice di scorrere nell'ordine di 2 50 elementi in un programma combinatorio a forza bruta. Per arrivare a n = 128 è quindi necessario utilizzare un approccio che non analizzi ogni stringa di bit. Quindi, piuttosto che lavorare in avanti dalle stringhe di bit alle sequenze di periodi, lavoro all'indietro: data una sequenza di periodi, esiste una stringa di bit che la realizza? Per n = 2 x c'è un limite superiore facile di 2 x (x + 1) / 2 sequenze di periodi (vs 2 2 x stringhe di bit).

Alcuni degli argomenti usano il lemma della periodicità delle stringhe :

Lasciare pe qessere due periodi di una stringa di lunghezza n. Se p + q ≤ n + gcd(p, q)quindi gcd(p, q)è anche un punto della stringa.

Wlog Presumo che inizieranno tutte le stringhe in esame 0.

Data una sequenza di periodi in cui è il periodo del prefisso di lunghezza 2 i ( sempre), ci sono alcune facili osservazioni sui possibili valori di :[p1 p2 ... pk]pip0 = 1pk+1

  • pk+1 ≥ pkpoiché un periodo di una stringa Sè anche un periodo di qualsiasi prefisso di S.

  • pk+1 = pk è sempre un'estensione possibile: basta ripetere la stessa stringa primitiva per il doppio del numero di caratteri.

  • 2k < pk+1 ≤ 2k+1è sempre una possibile estensione. Basta dimostrarlo perché una stringa aperiodica di lunghezza può essere estesa a una stringa aperiodica di lunghezza accodando qualsiasi lettera che non sia la sua prima lettera.pk+1 = 2k+1LL+1

    Prendi una stringa Sxdi lunghezza 2 k il cui periodo è e considera la stringa di lunghezza 2 k + 1 . Chiaramente ha un periodo di 2 k +1. Supponiamo che il suo periodo sia più piccolo.pkSxySSxySq

    Quindi anche per la periodicità il lemma è un periodo di , e poiché il più grande divisore è inferiore o uguale ai suoi argomenti ed è il periodo più piccolo, dobbiamo essere un fattore proprio di 2 k +1. Dal momento che il suo quoziente non può essere 2, abbiamo .2k+1 + q ≤ 2k+1+1 ≤ 2k+1 + gcd(2k+1, q)gcd(2k+1, q)SxySqqq ≤ (2k+1)/3

    Ora, poiché è un periodo di esso deve essere un periodo di . Ma il periodo di è . Abbiamo due casi:q ≤ 2kSxySSxSxpk

    1. gcd(pk, q) = pko si divide esattamente in .pkq
    2. pk + q > 2k + gcd(pk, q) in modo che il lemma della periodicità non imponga un periodo più piccolo.

    Considera innanzitutto il secondo caso. , contraddicendo la definizione di come il periodo di . Pertanto siamo costretti a concludere che questo è un fattore .pk > 2k + gcd(pk, q) - q ≥ 2k+1 - q ≥ 2k+1 - (2k+1)/3 ≥ 2qpkSxpkq

    Ma poiché qè un periodo di Sxed è il periodo di , il prefisso di lunghezza è solo copie del prefisso di lunghezza , quindi vediamo che è anche un periodo di .pkSxqq/pkpkpkSxyS

    Pertanto il periodo di SxySè uno o 2 k +1. Ma abbiamo due opzioni per ! Al massimo una scelta darà periodo , quindi almeno una darà periodo 2 k +1. QED.pkyypk

  • Il lemma della periodicità ci consente di respingere alcune delle possibili estensioni rimanenti.

  • Qualsiasi estensione che non ha superato un test di accettazione rapida o di rifiuto rapido deve essere testata in modo costruttivo.

La costruzione di una stringa di bit data una sequenza di periodi è essenzialmente un problema di soddisfacibilità, ma ha molta struttura. Esistono semplici vincoli di uguaglianza impliciti in ciascun periodo di prefisso, quindi utilizzo una struttura di dati del set di unione per combinare i bit in cluster indipendenti. Questo è bastato per affrontare n = 64, ma per n = 128 è stato necessario andare oltre. Uso due utili argomenti:2k - pk

  1. Se il prefisso di lunghezza Mè e il prefisso di lunghezza ha punto, allora il prefisso di lunghezza deve terminare . Questo è molto potente proprio nei casi che altrimenti avrebbero la maggior parte dei cluster indipendenti, il che è conveniente.01M-1L > MLL1M
  2. Se il prefisso di lunghezza Mè e il prefisso di lunghezza ha un periodo con e termina, allora deve in effetti terminare . Questo è più potente all'estremo opposto, quando la sequenza di periodi inizia con molti.0ML > ML - dd < M0d10d

Se non otteniamo una contraddizione immediata forzando il cluster con il primo bit (assunto come zero) ad essere uno, allora forza bruta (con alcune microottimizzazioni) sui possibili valori per i cluster non forzati. Si noti che l'ordine è in numero decrescente di quelli perché se il ith bit è uno, allora il periodo non può essere ie vogliamo evitare periodi che sono più brevi di quelli che sono già applicati dal clustering. Scendendo aumenta le possibilità di trovare presto un incarico valido.


Questo è davvero un grande risultato! Sono davvero colpito.

@Lembik, ho semplificato e ottimizzato il codice e ridotto il tempo di esecuzione per n = 128 di circa un terzo.
Peter Taylor,

1
Mi piacerebbe sapere quale algoritmo hai progettato per questo. Il tuo codice ha una logica notevolmente ridotta e deve fare qualcosa di molto intelligente.

7

Rust, 32, 10s 11s 29s sul mio laptop

Chiamalo con il formato bit come argomento della riga di comando.

Tecniche intelligenti: rappresentano le stringhe di bit direttamente come numeri, usa la bittwiddling per verificare i cicli. Cerca solo la prima metà delle stringhe di bit, quelle che iniziano con 0, poiché la matrice di periodi di una stringa di bit e il suo inverso (0s scambiati per 1s) sono identici. Se si è già verificata ogni possibilità per la posizione finale, non la cerco.

Altre cose intelligenti:

Per deduplicare ciascun blocco (stringhe in cui la prima metà dei bit sono uguali) uso un bitvector, che è molto più veloce di un hashset, poiché le lunghezze del ciclo finale non richiedono hash.

Inoltre, salto i primi passi del controllo del ciclo, perché so che il ciclo finale non può essere più breve del ciclo penultimo.

Dopo un sacco di profilazione, ora posso dire che quasi tutto il tempo viene utilizzato in modo produttivo, quindi penso che saranno necessari miglioramenti algoritmici per migliorare da qui. Sono anche passato a numeri interi a 32 bit per risparmiare un po 'più di tempo.

//extern crate cpuprofiler;
//use cpuprofiler::PROFILER;

extern crate bit_vec;
use bit_vec::BitVec;

use std::collections::HashSet;

fn cycle_len(num: u32, mask: u32, skip_steps: usize) -> usize {
    let mut left = num >> skip_steps;
    let mut mask = mask >> skip_steps;
    let mut steps = skip_steps;
    loop {
        left >>= 1;
        if left == (num & mask) {
            return steps;
        }
        mask >>= 1;
        steps += 1;
    }
}

fn all_cycles(size_log: usize) -> HashSet<Vec<usize>> {
    let mut set = HashSet::new();
    if size_log == 0 {
        set.insert(vec![]);
        return set;
    } else if size_log == 1 {
        set.insert(vec![0]);
        set.insert(vec![1]);
        return set;
    }
    let size: usize = 1 << size_log;
    let half_size: usize = 1 << size_log - 1;
    let shift_and_mask: Vec<(usize, u32)> = (1..size_log)
        .map(|subsize_log| {
            let subsize = 1 << subsize_log;
            (size - subsize, (1 << (subsize - 1)) - 1)
        })
        .collect();
    let size_mask = (1 << (size - 1)) - 1;
    for block in 0..(1 << (half_size - 1)) as u32 {
        let start: u32 = block << half_size;
        if block % 1024 == 0 {
            eprintln!(
                "{} ({:.2}%): {}",
                start,
                start as f64 / (1u64 << size - 1) as f64 * 100f64,
                set.len()
            );
        }
        let leader = {
            let mut cycles = Vec::new();
            for &(shift, mask) in &shift_and_mask {
                let subnum = start >> shift;
                cycles.push(cycle_len(subnum, mask, 0));
            }
            cycles
        };
        let &end = leader.last().unwrap();
        if (end..size).all(|count| {
            let mut new = leader.clone();
            new.push(count);
            set.contains(&new)
        })
        {
            continue;
        }
        let mut subset = BitVec::from_elem(size, false);
        for num in start..start + (1 << half_size) {
            subset.set(cycle_len(num, size_mask, end), true);
        }
        for (unique_cycle_len, _) in subset.into_iter().enumerate().filter(|x| x.1) {
            let mut new_l = leader.clone();
            new_l.push(unique_cycle_len);
            set.insert(new_l);
        }
    }
    set
}

fn main() {
    let size: f32 = std::env::args().nth(1).unwrap().parse().unwrap();
    let size_log = size.log2() as usize;
    //PROFILER.lock().unwrap().start("./my-prof.profile").unwrap();
    let cycles = all_cycles(size_log);
    //PROFILER.lock().unwrap().stop().unwrap();
    println!(
        "Number of distinct arrays of periods of bitstrings of length {} is {}",
        1 << size_log,
        cycles.len()
    );
}

Inserisci il bit-vec = "0.4.4"tuo Cargo.toml

Se desideri eseguirlo, clona questo: github.com/isaacg1/cycle quindi Cargo build --releaseper costruire, quindi Cargo run --release 32per eseguire.


Sembra che eprintln abbia bisogno di una versione di ruggine dopo 0.16.0. Funziona se lo cambio a println.

Questa risposta è davvero impressionante. timegli dà 27 secondi utente sul mio laptop.

@Lembik perché sei in una versione così vecchia di ruggine? Rust 1.0 è uscito anni fa.
isaacg,

Errore di battitura :) intendevo 1.16.0. blog.rust-lang.org/2017/03/16/Rust-1.16.html

Per i neofiti della ruggine, ti dispiacerebbe precisare esattamente come compilare il codice usando cargo?

4

Ruggine , 16

use std::collections::HashSet;
use std::io;

fn main() {
	print!("Enter a pow of two:");
	let mut input_text = String::new();
    io::stdin()
        .read_line(&mut input_text)
        .expect("failed to read from stdin");

    let n_as_string = input_text.trim();
	match n_as_string.parse::<usize>() {
		Ok(n) => {
			let log2 = (n as f64).log(2_f64) as usize;
			if n != 1 << log2 {
				panic!("{} is not a power of two", n);
			}
			let count = compute_count_array(log2, n);
			println!("n = {} -> count = {}", n, count);
		}
		Err(_) => { panic!("{} is not a number", n_as_string); }
	}
}

fn compute_count_array(log2:usize, n: usize) -> usize {
	let mut z = HashSet::new();

	let mut s:Vec<bool> = vec!(false; n);
	loop {
		let mut y:Vec<usize> = vec!();
		for j in 0..log2+1 {
			let p = find_period(&s[0..1<<j]);
			y.push(p);
		}		
		z.insert(y);
		if !next(&mut s) {
			break;
		}
	}
	z.len()
}

#[inline]
fn find_period(s: &[bool]) -> usize {
	let n=s.len();
	let mut j=1;
	while j<n {
		if s[0..n-j] == s[j..n] {
			return j;
		}
		j+=1;
    }
	n
}	

#[inline]
fn next(s:&mut Vec<bool>) -> bool {
	if s[0] {
		s[0] = false;
		for i in 1..s.len() {
			if s[i] {
				s[i] = false;
			} else {
				s[i] = true;
				return true;
			}
		}
		return false
	} else {
		s[0] = true;
	}
	true
}

Provalo online!

Compilazione: rustc -O <name>.rs

La stringa è implementata come vettore Bool.

  • La nextfunzione scorre attraverso le combinazioni;

  • La find_periodprende una fetta Bool e restituisce il periodo;

  • La compute_count_arrayfa il lavoro per ogni "potenza di due" sottosequenza di ogni combinazioni di Bools.

In teoria, non è previsto alcun overflow fino a quando non 2^nsupera il valore massimo di u64, ad es n > 64. Questo limite potrebbe essere superato con un test costoso su s = [true, true, ..., true].

Le cattive notizie sono: restituisce 317 per n = 16, ma non so perché. Non so neanche se lo farà in dieci minuti per n = 32, poiché Vec<bool>non è ottimizzato per questo tipo di calcolo.

MODIFICARE

  1. Sono riuscito a rimuovere il limite di 64 per n. Ora, non si narresta in modo anomalo fino a quando non è maggiore dell'intero di utilizzo massimo.

  2. Ho scoperto perché il codice precedente ha restituito 317 per n=32. Stavo contando serie di periodi e non matrici di periodi. C'erano tre matrici con gli stessi elementi:

    [1, 2, 3, 3, 8] -> {1, 2, 3, 8}
    [1, 2, 3, 8, 8] -> {1, 2, 3, 8}
    [1, 1, 3, 3, 7] -> {1, 3, 7}
    [1, 1, 3, 7, 7] -> {1, 3, 7}
    [1, 1, 3, 3, 8] -> {1, 3, 8}
    [1, 1, 3, 8, 8] -> {1, 3, 8}
    

Ora funziona. È ancora lento ma funziona.


Qui ci sono tutti 320 per n = 16 bpaste.net/show/3664e25ebc01 .

1
@Lembik Ho trovato la spiegazione per 317 grazie alla tua lista.
jferard,

2

C - 16

Non riesce su valori superiori a 16 cuz di overflow.

Non ho idea di quanto sia veloce perché funziona su un Chromebook che lo esegue su repl.it.

Implementa semplicemente la domanda mentre legge, passa attraverso tutte le stringhe di bit, calcola le matrici di punti e verifica se sono già state contate.

#include "stdio.h"
#include <stdbool.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>

int per(int s[], int l) {
  int period = 0;
  while (1) {
    period++;

    bool check = 1;
    int i;
    for (i=0; i<l-period; i++) {
      if (s[i]!=s[i+period]) {
        check = 0;
        break;
      }
    }
    if (check) {
      return period;
    }
  }
}

bool perar(int* s, int l, int* b, int i) {
  int n = 1;
  int j=0;
  while (n<=l) {
    b[i*l+j] = per(s, n);
    n=n<<1;
    j++;
  }

  for (j=0;j<i;j++) {
    int k;
    bool check = 1;
    for(k=0; k<l; k++) {
      if (b[j*l+k] != b[i*l+k]) {
        check = 0;
        break;
      }
    }
    if (check) {
      return 0;
    }
  }
  return 1;
}

int main(int argc, char* argv[]) {
  int n;
  scanf("%d", &n);
  puts("Running...");
  int i;
  int c = 0;
  int* a = malloc(n*sizeof(int));
  int m=pow(2, n);
  int* b = malloc(m*n*sizeof(int));
  for (i=0; i<m; i++) {
    int j;
    for (j=0; j<n; j++) {
      a[j] = (i>>j)&1;
    }
    c+=perar(a, n, b, i);
  }
  printf("Answer: %d\n", c);
  return 0;
}

Basta compilarlo con gcc, ecc.


Cordiali saluti - È stato un errore per 16allora quando il codice è stato cambiato in modo che i due mallocs fossero malloc(...int*))e ...**rispettivamente 16stampati Answer: 320come previsto, comunque 32stampati Answer: 0(e abbastanza rapidamente).
Jonathan Allan,

@JonathanAllan ha risolto il problema, appena fatto b an int *.
Maltysen,

@JonathanAllan la cosa 32 è perché 2 ** 32 trabocca l'int. Inoltre prenderò troppo memoria.
Maltysen,

@ThePirateBay ho fatto me e m longs, e questo segfault solo quando provo 32. repl.it/JwJl/2 Immagino che rimanga senza memoria.
Maltysen,

@Maltysen. Sembra che segfault perché hai incasinato qualcosa nell'allocazione / deallocazione piuttosto che nella mancanza di memoria disponibile. Ho ottenuto segfault per n = 8ma dopo che il risultato è stato stampato, il che significa che lo stack è danneggiato. Probabilmente stai scrivendo da qualche parte oltre i blocchi di memoria allocati.

2

Haskell

import qualified Data.Set as S
import Data.Bits

period :: Int -> Int -> Int
period num bits = go (bits-2) (div prefix 2) (clearBit prefix $ bits-1)
  where
  prefix = (2^bits-1) .&. num
  go p x y
    | x == y    = p
    | otherwise = go (p-1) (div x 2) (clearBit y p)

allPeriods :: Int ->  [[Int]]
allPeriods n = map periods [0..div(2^n)2-1]
  where
  periods num = map (period num) powers
  powers = takeWhile (<=n) $ iterate (*2) 2

main = readLn >>= print . S.size . S.fromList . allPeriods

Compila con ghc -O2. Provalo online!

Funziona in meno di 0,1 secondi sul mio hardware portatile di 6 anni per n=16. n=32prende 99 92min, quindi sono fuori fattore 9 o 10. Ho provato a memorizzare nella cache i periodi in una tabella di ricerca, quindi non devo ricalcolarli più e più volte, ma questo esaurisce rapidamente la memoria sulla mia macchina da 4 GB.


Nonostante sia un fattore di 10, il tuo codice è molto bello.

@Lembik. Grazie. Sto solo provando un miglioramento: il codice sopra riportato calcola i periodi per sottostringhe di lunghezza 1, ma è del tutto superfluo. Oltre a non calcolarli, risparmia anche un po 'di tempo quando si trovano array di periodi unici, perché sono tutti più brevi di un elemento.
nimi,

@Lembik: omettendo le sottostringhe di lunghezza 1 si risparmia circa 7 minuti per n = 32. Ancora troppo a lungo.
nimi,

C'è un algoritmo lineare veloce per calcolare il periodo che potrebbe aiutare.

Non puoi davvero creare una tabella di ricerca di dimensioni 2 ^ 16? Non sembra troppo grande.

1

Python 2 (PyPy), 16

import sys
import math
def do(n):
 masks=[]
 for i in range(n):
  masks+=[(1<<((2<<i)-1))-1]
 s=set()
 bits=1<<n
 for i in xrange(1<<bits):
  r=[0,]*n
  for j in range(len(masks)):
   mask=masks[j]
   k,c=i>>bits-(2<<j),1
   d=k>>1
   while k&mask^d:
    d>>=1
    mask>>=1
    c+=1
   r[j]=c
  s|={tuple(r)}
 return len(s)
print do(int(math.log(int(sys.argv[1]),2)))

: | perché 32 deve impiegare così tanto tempo
solo ASCII il

So di poterne saltare la metà, ma IDK come: /
ASCII, solo il

Il tuo codice sembra generare solo "Nessuno" per me. Come lo stai eseguendo? osboxes@osboxes:~/python$ python ascii_user.py 16 None

merda, questo in realtà non è quello che corro
solo ASCII il

@Lembik corretto ora
ASCII il

1

[C ++], 32, 4 minuti

#include <iostream>
#include <vector>

typedef unsigned int u;
template<typename T, typename U>
u Min(T a, U b) {
    return a < b ? a : b;
}

template<typename T, typename U>
u Max(T a, U b) {
    return a > b ? a : b;
}

u Mask(int n) {
    if (n < 0) n = 0;
    return ~((u)(-1) << n);
}
u MASKS[32];

inline u Rshift(u v, int n) {
    return n < 0 ? v >> (-1*n)
    : n > 0 ? v << n
    : n;
}

int GetNextPeriodId(u pattern, int pattern_width, int prior_id) {
    int period = (prior_id % (pattern_width>>1)) + 1;
    int retval = prior_id * pattern_width;

    for (; period < pattern_width; period+=1) {
        u shift = pattern >> period;
        int remainder = pattern_width-period;
        u mask = MASKS[period];

        for (;remainder >= period && !((pattern ^ shift) & mask);
             shift >>= period, remainder -= period);

        if (remainder > period) continue;
        if (remainder == 0 || !((pattern ^ shift) & MASKS[remainder])) {
            retval += (period-1);
            break;
        }
    }
    if (period == pattern_width) {
        retval += pattern_width-1;
    }
    return retval;
}

int ParseInput(int argc, char** argv) {
    if (argc > 1) {
        switch(atoi(argv[1])) {
            case 1:
                return 1;
            case 2:
                return 2;
            case 4:
                return 4;
            case 8:
                return 8;
            case 16:
                return 16;
            case 32:
                return 32;
            default:
                return 0;
        }
    }
    return 0;
}

void PrintId(u id, int patternWidth) {
    for(;patternWidth > 0; id /= patternWidth, patternWidth >>= 1) {
        std::cout << (id % patternWidth)+1 << ",";
    }
    std::cout << std::endl;
}

int TestAndSet(std::vector<bool>& v, int i) {
    int retval = v[i] ? 0 : 1;
    v[i] = true;
    return retval;
}

std::vector<bool> uniques(1<<15);
int uniqueCount = 0;

void FillUniques(u i, int id, int target_width, int final_width) {
    int half_size = target_width / 2;
    u end = 1u<<(half_size-1);
    u mask = MASKS[half_size];
    u lowers[] = { i, (~i)&mask };
    for (u j = 0ul; j < end; j++) {
        u upper = j << half_size;
        u patterns[] = { (upper|lowers[0]), (upper|lowers[1]) };
        for (int k=0; k < sizeof(patterns)/sizeof(patterns[0]); k+=1) {
            int fid = GetNextPeriodId(patterns[k], target_width, id);
            if (target_width != final_width) {
                FillUniques(patterns[k], fid, target_width*2, final_width);
            } else {
                if (TestAndSet(uniques, fid)) {
                    uniqueCount += 1;
                }
            }
        }
    }
}

int main(int argc, char** argv) {
    for (int i = 0; i < 32; i++) {
        MASKS[i] = Mask(i);
    }
    int target_width = 32; // ParseInput(argc, argv);
    if (!target_width) {
        std::cout << "Usage: " << argv[0] << " [1|2|4|8|16|32]" << std::endl;
        return 0;
    }
    if (target_width == 1) {
        std::cout << 1 << std::endl;
        return 0;
    }
    FillUniques(0, 0, 2, target_width);
    std::cout << uniqueCount << std::endl;
    return 0;
}
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.