Conta le matrici che creano set unici


11

Questa domanda ha una configurazione simile a Trova un array che si adatta a una serie di somme sebbene sia abbastanza diverso nei suoi obiettivi.

Considera una matrice Adi lunghezza n. La matrice contiene solo numeri interi positivi. Per esempio A = (1,1,2,2). Definiamo f(A)come l'insieme delle somme di tutti i sottocampi contigui non vuoti di A. In questo caso f(A) = {1,2,3,4,5,6}. I passaggi per produrre f(A) sono i seguenti:

I subarrays di Asono (1), (1), (2), (2), (1,1), (1,2), (2,2), (1,1,2), (1,2,2), (1,1,2,2). Le loro rispettive somme sono 1,1,2,2,2,3,4,4,5,6. Il set che ottieni da questo elenco è quindi {1,2,3,4,5,6}.

Chiamiamo un array A univoco se non esiste un altro array Bdella stessa lunghezza tale che f(A) = f(B), ad eccezione dell'array Ainvertito. Ad esempio, f((1,2,3)) = f((3,2,1)) = {1,2,3,5,6}ma non esiste un altro array di lunghezza 3che produce lo stesso insieme di somme.

Prenderemo in considerazione solo le matrici in cui gli elementi sono o un dato intero so s+1. Ad esempio, se s=1gli array contengono solo 1e 2.

Compito

Il compito, per un dato ned sè quello di contare il numero di matrici uniche di quella lunghezza. Puoi presumere che ssia tra 1e 9.

Non si deve contare il retro di un array così come l'array stesso.

Esempi

s = 1, la risposta è sempre n+1.

s = 2, le risposte contando dall'alto n = 1sono:

2,3,6,10,20,32,52,86

s = 8, le risposte contando dall'alto n = 1sono:

2,3,6,10,20,36,68,130

Punto

Per un dato dato n, il tuo codice dovrebbe generare la risposta per tutti i valori di sda 1a 9. Il tuo punteggio è il valore più alto nper il quale questo viene completato in un minuto.

analisi

Dovrò eseguire il tuo codice sulla mia macchina Ubuntu, quindi per favore includi le istruzioni più dettagliate possibili su come compilare ed eseguire il tuo codice.

Classifica

  • n = 24 di Anders Kaseorg in Rust (34 secondi)
  • n = 16 di Ourous in Clean (36 secondi)
  • n = 14 di JRowan in Common Lisp (49 secondi)

Quindi se s = 8 allora è un array di tutte le possibili combinazioni di 8 e 9, nient'altro?
JRowan,

@JRowan No. Non contate nessuno di quegli array che hanno lo stesso insieme di somme di qualche altro array.
Anush,

Questa parte di cui sono un po 'confuso Considereremo solo le matrici in cui gli elementi sono un dato numero intero s + s + 1. Ad esempio, se s = 1 gli array contengono solo 1 e 2. Quindi se n è 2 e s è 3 quali sarebbero gli array da testare?
JRowan,

che dire di [3,3] e sto attualmente rimuovendo il retro delle liste es. [3,4] -> [4,3]
JRowan,

2
@RosLuP In primo luogo, intendevi postarlo sull'altra domanda , e in secondo luogo, [3, 5, 4] è un sottoinsieme ma non un sottoarray di [3, 5, 1, 4].
Anders Kaseorg,

Risposte:


5

Ruggine , n ≈ 24

Richiede ruggine notturna per la comoda reverse_bitsfunzionalità. Compilare con rustc -O unique.rsed eseguire con (ad esempio) ./unique 24.

#![feature(reverse_bits)]
use std::{collections::HashMap, env, mem, process};

type T = u32;
const BITS: u32 = mem::size_of::<T>() as u32 * 8;

fn main() {
    let args = env::args().collect::<Vec<_>>();
    assert!(args.len() == 2);
    let n: u32 = args[1].parse().unwrap();
    assert!(n > 0);
    assert!(n <= BITS);
    let mut unique = (2..=9).map(|_| HashMap::new()).collect::<Vec<_>>();
    let mut sums = vec![0 as T; n as usize];
    for a in 0 as T..=!0 >> (BITS - n) {
        if a <= a.reverse_bits() >> (BITS - n) {
            for v in &mut sums {
                *v = 0;
            }
            for i in 0..n {
                let mut bit = 1;
                for j in i..n {
                    bit <<= a >> j & 1;
                    sums[(j - i) as usize] |= bit;
                }
            }
            for s in 2..=9 {
                let mut sums_s =
                    vec![0 as T; ((n + (n - 1) * s) / BITS + 1) as usize].into_boxed_slice();
                let mut pos = 0;
                let mut shift = 0;
                let mut lo = 0;
                let mut hi = 0;
                for &v in &sums {
                    lo |= v << shift;
                    if BITS - shift < n {
                        hi |= v >> (BITS - shift);
                    }
                    shift += s;
                    if shift >= BITS {
                        shift -= BITS;
                        sums_s[pos] = lo;
                        pos += 1;
                        lo = hi;
                        hi = 0;
                    }
                }
                if lo != 0 || hi != 0 {
                    sums_s[pos] = lo;
                    pos += 1;
                    if hi != 0 {
                        sums_s[pos] = hi;
                    }
                }
                unique[s as usize - 2]
                    .entry(sums_s)
                    .and_modify(|u| *u = false)
                    .or_insert(true);
            }
        }
    }
    let mut counts = vec![n + 1];
    counts.extend(
        unique
            .iter()
            .map(|m| m.values().map(|&u| u as T).sum::<T>())
            .collect::<Vec<_>>(),
    );
    println!("{:?}", counts);
    process::exit(0); // Avoid running destructors.
}

Questo è fantastico, grazie. Si completa per n = 25 in circa 90 secondi. Ma il problema principale è che utilizza il 70% dei miei 8 GB di RAM.
Anush,

Mi sono improvvisamente preoccupato per qualcosa. Stai verificando che gli array siano unici rispetto a tutti gli altri array possibili o solo array con valori se s+1in essi?
Anush,

@Anush Sì, ho scambiato un po 'di utilizzo della memoria per la velocità. Sto contando gli array che sono unici con altri array con valori se s + 1(poiché hai detto che sono gli unici array che prenderemo in considerazione), anche se non è immediatamente ovvio se ciò farebbe la differenza.
Anders Kaseorg,

1
Penso che dovrò risolverlo domani. Le matrici 1,1,2,2 e 1,1,1,3 danno entrambe la serie di somme 1,2,3,4,5,6. Tuttavia, il primo non è unico tra gli array con solo 1 e 2, quindi sono un po 'confuso se ora fa la differenza.
Anush,

2
@Anush Fa la differenza: le somme di [1, 2, 2, 2] sono uniche tra le matrici con 1 e 2 di lunghezza 4, ma uguali alle somme di [1, 1, 2, 3].
Anders Kaseorg,

2

SBL Lisp comune, N = 14

funzione di chiamata (goahead ns)

    (defun sub-lists(l m &optional(x 0)(y 0))
  (cond; ((and(= y (length l))(= x (length l)))nil)
        ((= y (length l))m)
        ((= x (length l))(sub-lists l m 0(1+ y)))
    (t (sub-lists l (cons(loop for a from x to (+ x y)

             when (and(nth (+ x y)l)(nth a l)(< (+ x y)(length l)))
                ;   while (nth a l)
             ;while(and(< (+ x y)(length l))(nth a l))
                    collect (nth a l))m) (1+ x)y))
    ))
(defun permutations(size elements)
  (if (zerop size)'(())
 (mapcan (lambda (p)
                    (map 'list (lambda (e)
                           (cons e p))
                         elements))
     (permutations (1- size) elements))))
(defun remove-reverse(l m)
  (cond ((endp l)m)
    ((member (reverse (first l))(rest l) :test #'equal)(remove-reverse (rest l)m))
    (t (remove-reverse (rest l)(cons (first l)m)))))
(defun main(n s)
  (let((l (remove-reverse (permutations n `(,s ,(1+ s)))nil)))

  (loop for x in l
     for j = (remove 'nil (sub-lists x nil))
       collect(sort (make-set(loop for y in j
        collect (apply '+ y))nil)#'<)
     )
  ))
(defun remove-dups(l m n)
  (cond ((endp l)n)
        ((member (first l) (rest l) :test #'equal)(remove-dups(rest l)(cons (first l) m) n))
    ((member (first l) m :test #'equal)(remove-dups(rest l)m n))
    (t(remove-dups (rest l) m (cons (first l) n))))

  )
(defun goahead(n s)
  (loop for a from 1 to s
  collect(length (remove-dups(main n a)nil nil))))
(defun make-set (L m)
  "Returns a set from a list. Duplicate elements are removed."
  (cond ((endp L) m)
    ((member (first L) (rest L)) (make-set (rest L)m))
    ( t (make-set (rest L)(cons (first l)m)))))

ecco i tempi di esecuzione

CL-USER> (time (goahead 14 9))
Evaluation took:
  34.342 seconds of real time
  34.295000 seconds of total run time (34.103012 user, 0.191988 system)
  [ Run times consist of 0.263 seconds GC time, and 34.032 seconds non-GC time. ]
  99.86% CPU
  103,024,254,028 processor cycles
  1,473,099,744 bytes consed

(15 1047 4893 6864 7270 7324 7328 7328 7328)
CL-USER> (time (goahead 15 9))
Evaluation took:
  138.639 seconds of real time
  138.511089 seconds of total run time (137.923824 user, 0.587265 system)
  [ Run times consist of 0.630 seconds GC time, and 137.882 seconds non-GC time. ]
  99.91% CPU
  415,915,271,830 processor cycles
  3,453,394,576 bytes consed

(16 1502 8848 13336 14418 14578 14594 14594 14594)

Come eseguo questo? Copio il tuo codice in un file e lo chiamo in sbclqualche modo?
Anush,

1
Uso emacs e melma ma potresti metterlo in un file dire test.lisp e in sbcl promp alla tua chiamata alla directory (carica "test.lisp") e quindi chiamare la funzione come ho in fondo
JRowan

2

Pulito

Certamente non è l'approccio più efficiente, ma sono interessato a vedere come funziona un filtro per valore ingenuo.

Detto questo, c'è ancora un po 'di miglioramento da fare usando questo metodo.

module main
import StdEnv, Data.List, System.CommandLine

f l = sort (nub [sum t \\ i <- inits l, t <- tails i])

Start w
	# ([_:args], w) = getCommandLine w
	= case map toInt args of
		[n] = map (flip countUniques n) [1..9]
		_ = abort "Wrong number of arguments!"

countUniques 1 n = inc n
countUniques s n = length uniques
where
	lists = [[s + ((i >> p) bitand 1) \\ p <- [0..dec n]] \\ i <- [0..2^n-1]]
	pairs = sortBy (\(a,_) (b,_) = a < b) (zip (map f lists, lists))
	groups = map (snd o unzip) (groupBy (\(a,_) (b,_) = a == b) pairs)
	uniques = filter (\section = case section of [a, b] = a == reverse b; [_] = True; _ = False) groups

Inserire in un file denominato main.iclo modificare la riga superiore in module <your_file_name_here>.

Compila con clm -h 1500m -s 50m -fusion -t -IL Dynamics -IL StdEnv -IL Platform main.

Puoi ottenere la versione che TIO (e io stesso) utilizzi dal link nell'intestazione o una più recente da qui .


Non credo che questo codice dia il giusto risultato. L'ho provato con s = 8 e dà [9,86,126,130,130,130,130,130,130]
Anush

@Anush hmm so di averlo provato. Vedrò se ho cambiato qualcosa tra quello e quello pubblicato, dammi qualche ora e posso farlo durante la mia pausa.
Οuroso

@Anush Perché stai fornendo s? Nella domanda che affermi " Per una data n , il tuo codice dovrebbe generare la risposta per tutti i valori di s da 1 a 9."
Οuroso

1
Penso che sia quello che tu chiami un congelamento del cervello da parte mia :) Ora cronometrerò il tuo codice.
Anush,
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.