Zählen Sie Arrays, die eindeutige Mengen bilden


11

Diese Frage hat einen ähnlichen Aufbau wie " Ein Array suchen", das zu einer Reihe von Summen passt, obwohl sich seine Ziele stark unterscheiden.

Betrachten Sie ein Array Avon Länge n. Das Array enthält nur positive Ganzzahlen. Zum Beispiel A = (1,1,2,2). Definieren wir f(A)als die Menge der Summen aller nicht leeren zusammenhängenden Subarrays von A. In diesem Fall f(A) = {1,2,3,4,5,6}. Die zu produzierenden Schritte f(A) sind wie folgt:

Die Subarrays von Asind (1), (1), (2), (2), (1,1), (1,2), (2,2), (1,1,2), (1,2,2), (1,1,2,2). Ihre jeweiligen Beträge sind 1,1,2,2,2,3,4,4,5,6. Das Set, das Sie aus dieser Liste erhalten, ist daher {1,2,3,4,5,6}.

Wir nennen ein Array A eindeutig, wenn es kein anderes Array Bmit der gleichen Länge gibt f(A) = f(B), außer dem Aumgekehrten Array . Zum Beispiel, f((1,2,3)) = f((3,2,1)) = {1,2,3,5,6}aber es gibt kein anderes Längenarray 3, das den gleichen Satz von Summen erzeugt.

Wir werden nur Arrays betrachten, bei denen die Elemente entweder eine bestimmte Ganzzahl soder sind s+1. ZB wenn s=1die Arrays nur 1und enthalten würden 2.

Aufgabe

Die Aufgabe für eine gegebene nund sist es, die Anzahl der eindeutigen Arrays dieser Länge zu zählen. Sie können davon ausgehen, dass szwischen 1und 9.

Sie sollten nicht die Umkehrung eines Arrays sowie das Array selbst zählen.

Beispiele

s = 1ist die Antwort immer n+1.

s = 2Die Antworten zählen von n = 1oben:

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

s = 8Die Antworten zählen von n = 1oben:

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

Ergebnis

Für einen bestimmten nFall sollte Ihr Code die Antwort für alle Werte von svon 1bis ausgeben 9. Ihre Punktzahl ist der höchste Wert, nfür den dies in einer Minute abgeschlossen ist.

Testen

Ich muss Ihren Code auf meinem Ubuntu-Computer ausführen. Geben Sie daher so detaillierte Anweisungen wie möglich zum Kompilieren und Ausführen Ihres Codes an.

Bestenliste

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

Wenn also s = 8 ist, dann ist es ein Array aller möglichen Kombinationen von 8 und 9, sonst nichts?
JRowan

@JRowan Nein. Sie zählen keines dieser Arrays, die die gleichen Summen wie ein anderes Array haben.
Anush

In diesem Teil bin ich etwas verwirrt. Wir werden nur Arrays betrachten, bei denen die Elemente entweder eine bestimmte ganze Zahl s oder s + 1 sind. Wenn beispielsweise s = 1 wäre, würden die Arrays nur 1 und 2 enthalten. Wenn also n 2 und s 3 ist, welche Arrays wären zu testen?
JRowan

Was ist mit [3,3] und ich entferne gerade die Rückseite der Listen, z. [3,4] -> [4,3]
JRowan

2
@RosLuP Erstens wollten Sie dies auf der anderen Frage posten , und zweitens ist [3, 5, 4] eine Teilmenge, aber kein Teilarray von [3, 5, 1, 4].
Anders Kaseorg

Antworten:


5

Rust , n ≈ 24

Benötigt nächtlichen Rost für die praktische reverse_bitsFunktion. Kompilieren mit rustc -O unique.rsund ausführen mit (z ./unique 24. B.) .

#![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.
}

Das ist großartig, danke. Es dauert für n = 25 in ungefähr 90 Sekunden. Das Hauptproblem ist jedoch, dass 70% meiner 8 GB RAM verwendet werden.
Anush

Ich habe mir plötzlich Sorgen um etwas gemacht. Überprüfen Sie, ob die Arrays in Bezug auf alle anderen möglichen Arrays eindeutig sind, oder nur Arrays mit Werten sund s+1darin?
Anush

@Anush Ja, ich habe die Speichernutzung gegen Geschwindigkeit eingetauscht. Ich zähle Arrays, die für andere Arrays mit Werten eindeutig sind, sund s + 1(da Sie sagten, dies sind die einzigen Arrays, die wir berücksichtigen werden), obwohl nicht sofort klar ist, ob dies einen Unterschied machen würde.
Anders Kaseorg

1
Ich denke, ich muss das morgen klären. Die Arrays 1,1,2,2 und 1,1,1,3 geben beide den Satz von Summen 1,2,3,4,5,6 an. Ersteres ist jedoch nicht einzigartig unter Arrays mit nur 1 und 2, so dass ich ein wenig verwirrt bin, wenn es jetzt einen Unterschied macht.
Anush

2
@Anush Es macht einen Unterschied: Die Summen von [1, 2, 2, 2] sind unter Arrays mit 1 und 2 der Länge 4 eindeutig, entsprechen aber den Summen von [1, 1, 2, 3].
Anders Kaseorg

2

Common Lisp SBCL, N = 14

Aufruffunktion (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)))))

Hier sind die Laufzeiten

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)

Wie führe ich das aus? Kopiere ich Ihren Code in eine Datei und rufe ihn sbclirgendwie auf?
Anush

1
Ich benutze Emacs und Slime, aber Sie könnten es in eine Datei setzen, sagen Sie test.lisp und in sbcl promp bei Ihrem Verzeichnisaufruf (laden Sie "test.lisp") und dann die Funktion aufrufen, wie ich es unten habe
JRowan

2

Sauber

Sicher nicht der effizienteste Ansatz, aber ich bin gespannt, wie gut ein naiver By-Value-Filter funktioniert.

Trotzdem müssen mit dieser Methode noch einige Verbesserungen vorgenommen werden.

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

In eine Datei mit dem Namen main.icleinfügen oder die oberste Zeile in ändern module <your_file_name_here>.

Kompilieren mit clm -h 1500m -s 50m -fusion -t -IL Dynamics -IL StdEnv -IL Platform main.

Sie können die Version, die TIO (und ich) verwenden, über den Link in der Überschrift oder eine neuere von hier herunterladen .


Ich denke nicht, dass dieser Code die richtige Ausgabe liefert. Ich habe es mit s = 8 versucht und es gibt [9,86,126,130,130,130,130,130,130]
Anush

@ Anush hmm ich weiß ich habe es getestet. Ich werde sehen, ob ich etwas zwischen dem und dem geposteten geändert habe, gib mir ein paar Stunden und ich kann es in meiner Pause tun.
ousurous

@ Anush Warum bieten Sie s? In der Frage geben Sie an: " Für ein gegebenes n sollte Ihr Code die Antwort für alle Werte von s von 1 bis 9 ausgeben."
ousurous

1
Ich denke, das nennst du meinerseits ein Einfrieren des Gehirns :) Ich werde deinen Code jetzt zeitlich festlegen.
Anush
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.