Compter les tableaux qui créent des ensembles uniques

11

Cette question a une configuration similaire pour rechercher un tableau qui correspond à un ensemble de sommes, bien que ses objectifs soient très différents.

Considérez un tableau Ade longueur n. Le tableau ne contient que des entiers positifs. Par exemple A = (1,1,2,2). Définissons f(A)comme l'ensemble des sommes de tous les sous-réseaux contigus non vides de A. Dans ce cas f(A) = {1,2,3,4,5,6}. Les étapes de production f(A) sont les suivantes:

Les sous-réseaux de Asont (1), (1), (2), (2), (1,1), (1,2), (2,2), (1,1,2), (1,2,2), (1,1,2,2). Leurs sommes respectives sont 1,1,2,2,2,3,4,4,5,6. L'ensemble que vous obtenez de cette liste est donc {1,2,3,4,5,6}.

Nous appelons un tableau A unique s'il n'y a pas d'autre tableau Bde la même longueur tel que f(A) = f(B), à l'exception du tableau Ainversé. Par exemple, f((1,2,3)) = f((3,2,1)) = {1,2,3,5,6}mais aucun autre tableau de longueur 3ne produit le même ensemble de sommes.

Nous ne considérerons que les tableaux où les éléments sont soit un entier donné, ssoit s+1. Par exemple, si s=1les tableaux contiennent uniquement 1et 2.

Tâche

La tâche, pour une donnée net sest de compter le nombre de tableaux uniques de cette longueur. Vous pouvez supposer que sc'est entre 1et 9.

Vous ne devez pas compter l'inverse d'un tableau ainsi que le tableau lui-même.

Exemples

s = 1, la réponse est toujours n+1.

s = 2, les réponses qui comptent à partir de n = 1:

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

s = 8, les réponses qui comptent à partir de n = 1:

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

But

Pour une donnée n, votre code devrait afficher la réponse pour toutes les valeurs de sà 1à 9. Votre score est la valeur la plus élevée npour laquelle cela se termine en une minute.

Essai

Je devrai exécuter votre code sur ma machine Ubuntu, veuillez donc inclure des instructions aussi détaillées que possible sur la façon de compiler et d'exécuter votre code.

Classement

  • n = 24 par Anders Kaseorg dans Rust (34 secondes)
  • n = 16 par Ourous dans Clean (36 secondes)
  • n = 14 par JRowan en Common Lisp (49 secondes)
Anush
la source
Donc, si s = 8, alors c'est un tableau de toutes les combinaisons possibles de 8 et 9, rien d'autre?
JRowan
@JRowan Non. Vous ne comptez aucun de ces tableaux qui ont le même ensemble de sommes qu'un autre tableau.
Anush
Cette partie est un peu confuse. Nous ne considérerons que les tableaux où les éléments sont soit un entier donné s soit s + 1. Par exemple, si s = 1, les tableaux ne contiendraient que 1 et 2. Donc, si n est 2 et s est 3, quels seraient les tableaux à tester?
JRowan
qu'en est-il de [3,3] et je suis en train de supprimer l'inverse des listes, par exemple. [3,4] -> [4,3]
JRowan
2
@RosLuP Premièrement, vous vouliez publier cela sur l'autre question , et deuxièmement, [3, 5, 4] est un sous - ensemble mais pas un sous-tableau de [3, 5, 1, 4].
Anders Kaseorg

Réponses:

5

Rouille , n ≈ 24

Nécessite une rouille nocturne pour la reverse_bitsfonction pratique . Compilez avec rustc -O unique.rset exécutez avec (par exemple) ./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.
}
Anders Kaseorg
la source
C'est super, merci. Il se termine pour n = 25 en environ 90 secondes. Mais le principal problème est qu'il utilise 70% de mes 8 Go de RAM.
Anush
Je me suis soudain inquiété pour quelque chose. Vérifiez-vous que les tableaux sont uniques par rapport à tous les autres tableaux possibles, ou simplement des tableaux avec des valeurs set s+1en eux?
Anush
@Anush Oui, j'ai échangé une certaine utilisation de la mémoire contre de la vitesse. Je compte les tableaux qui sont uniques par rapport aux autres tableaux avec des valeurs set s + 1(puisque vous avez dit que ce sont les seuls tableaux que nous considérerons), bien qu'il ne soit pas immédiatement évident que cela ferait une différence.
Anders Kaseorg
1
Je pense que je devrai régler ça demain. Les tableaux 1,1,2,2 et 1,1,1,3 donnent tous deux l'ensemble des sommes 1,2,3,4,5,6. Cependant, le premier n'est pas unique parmi les tableaux avec seulement 1 et 2, donc je suis un peu confus si cela fait une différence maintenant.
Anush
2
@Anush Cela fait une différence: les sommes de [1, 2, 2, 2] sont uniques parmi les tableaux avec 1 et 2 de longueur 4, mais égales aux sommes de [1, 1, 2, 3].
Anders Kaseorg
2

Common Lisp SBCL, N = 14

fonction d'appel (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)))))

voici les temps d'exécution

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)
JRowan
la source
Comment exécuter cela? Dois-je copier votre code dans un fichier et l'appeler d'une sbclmanière ou d'une autre?
Anush
1
J'utilise emacs et slime mais vous pouvez le mettre dans un fichier dis test.lisp et dans sbcl promp lors de votre appel de répertoire (chargez "test.lisp") puis appelez la fonction comme je l'ai en bas
JRowan
2

Nettoyer

Ce n'est certainement pas l'approche la plus efficace, mais je suis curieux de voir à quel point un filtre de valeur naïf fonctionne bien.

Cela dit, il y a encore un peu d'amélioration à faire en utilisant cette méthode.

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

Placer dans un fichier nommé main.iclou changer la ligne supérieure enmodule <your_file_name_here> .

Compiler avec clm -h 1500m -s 50m -fusion -t -IL Dynamics -IL StdEnv -IL Platform main .

Vous pouvez obtenir la version TIO (et moi-même) à partir du lien dans l'en-tête, ou une version plus récente d' ici .

Οurous
la source
Je ne pense pas que ce code donne la bonne sortie. Je l'ai essayé avec s = 8 et ça donne [9,86,126,130,130,130,130,130,130]
Anush
@Anush hmm je sais que je l'ai testé. Je vais voir si j'ai changé quelque chose entre ça et celui affiché, donnez-moi quelques heures et je peux le faire pendant ma pause.
6urous
@Anush Pourquoi fournissez-vous s? Dans la question, vous indiquez " Pour un n donné , votre code doit afficher la réponse pour toutes les valeurs de s de 1 à 9."
Οurous
1
Je pense que c'est ce que vous appelez un gel du cerveau de ma part :) Je vais chronométrer votre code maintenant.
Anush