Подсчет массивов, которые делают уникальные наборы

11

Этот вопрос имеет аналогичную настройку для поиска массива, который соответствует набору сумм, хотя цели его совершенно различны.

Рассмотрим массив Aдлины n. Массив содержит только натуральные числа. Например A = (1,1,2,2). Определим f(A)как множество сумм всех непустых непрерывных подмассивов A. В этом случае f(A) = {1,2,3,4,5,6}. Шаги для производства f(A) следующие:

Подмассивы Aесть (1), (1), (2), (2), (1,1), (1,2), (2,2), (1,1,2), (1,2,2), (1,1,2,2). Их соответствующие суммы 1,1,2,2,2,3,4,4,5,6. Таким образом, набор, который вы получаете из этого списка {1,2,3,4,5,6}.

Мы называем массив A уникальным, если нет другого массива Bтакой же длины f(A) = f(B), кроме массива в Aобратном порядке. Как пример, f((1,2,3)) = f((3,2,1)) = {1,2,3,5,6}но нет другого массива длины, 3который производит тот же набор сумм.

Мы будем рассматривать только массивы, в которых элементы являются заданным целым числом sили s+1. Например, если s=1массивы будут содержать только 1и 2.

задача

Задача для заданного nи sсостоит в том, чтобы подсчитать количество уникальных массивов этой длины. Вы можете предположить, что sмежду 1и 9.

Вы не должны считать обратный массив, а также сам массив.

Примеры

s = 1ответ всегда n+1.

s = 2, ответы считая от n = 1:

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

s = 8, ответы считая от n = 1:

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

Гол

Для данного n, ваш код должен выводить ответ для всех значений sот 1до 9. Ваша оценка - это наибольшее значение, nдля которого это завершается за одну минуту.

тестирование

Мне нужно будет запустить ваш код на моей машине с Ubuntu, поэтому, пожалуйста, включите как можно более подробные инструкции по компиляции и запуску вашего кода.

Leaderboard

  • Андерс Касеорг в Русте n = 24 (34 секунды)
  • n = 16 от Ourous in Clean (36 секунд)
  • n = 14 от JRowan в Common Lisp (49 секунд)
Ануш
источник
Так что, если s = 8, то это массив всех возможных комбинаций 8 и 9, больше ничего?
JRowan
@JRowan Нет. Вы не учитываете ни один из тех массивов, которые имеют тот же набор сумм, что и некоторые другие массивы.
Ануш
Эта часть, в которой я немного запутался. Мы рассмотрим только массивы, в которых элементы являются заданным целым числом s или s + 1. Например, если s = 1, массивы будут содержать только 1 и 2. Итак, если n равно 2, а s равно 3, какими будут тестируемые массивы?
JRowan
как насчет [3,3], и я в настоящее время удаляю реверс списков, например [3,4] -> [4,3]
JRowan
2
@RosLuP Во-первых, вы намеревались опубликовать это по другому вопросу , а во-вторых, [3, 5, 4] является подмножеством, но не подмассивом [3, 5, 1, 4].
Андерс Касеорг

Ответы:

5

Ржавчина , n ≈ 24

Требуется ночная ржавчина для удобной reverse_bitsфункции. Скомпилируйте rustc -O unique.rsи запустите (например) ./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.
}
Андерс Касеорг
источник
Это здорово, спасибо. Это завершается для n = 25 примерно за 90 секунд. Но главная проблема в том, что он использует 70% моей 8 ГБ оперативной памяти.
Ануш
Я вдруг забеспокоился о чем-то. Вы проверяете, что массивы уникальны по отношению ко всем другим возможным массивам, или просто массивы со значениями sи s+1в них?
Ануш
@Anush Да, я обменял некоторое использование памяти на скорость. Я считаю массивы, которые являются уникальными по сравнению с другими массивами со значениями sи s + 1(так как вы сказали, что это единственные массивы, которые мы рассмотрим), хотя не сразу очевидно, будет ли это иметь значение.
Андерс Касеорг
1
Я думаю, что мне нужно будет решить это завтра. Массивы 1,1,2,2 и 1,1,1,3 дают набор сумм 1,2,3,4,5,6. Однако первое не является уникальным среди массивов, имеющих только 1 и 2, поэтому я немного растерялся, если сейчас что-то изменилось.
Ануш
2
@Anush Это действительно имеет значение: суммы [1, 2, 2, 2] уникальны среди массивов с 1 и 2 длиной 4, но равны суммам [1, 1, 2, 3].
Андерс Касеорг
2

Common Lisp SBCL, N = 14

функция вызова (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)))))

вот время выполнения

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
источник
Как мне это запустить? Скопировать ли ваш код в файл и как- sbclнибудь вызвать его ?
Anush
1
Я использую emacs и slime, но вы можете поместить его в файл скажем test.lisp и в sbcl promp при вызове вашего каталога (загрузить «test.lisp»), а затем вызвать функцию так, как она у меня внизу
JRowan
2

чистый

Конечно, не самый эффективный подход, но мне интересно посмотреть, насколько хорошо работает наивный фильтр значений.

Тем не менее, есть еще некоторые улучшения, которые будут сделаны с помощью этого метода.

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

Поместите в файл с именем main.iclили измените верхнюю строку на module <your_file_name_here>.

Компилировать с clm -h 1500m -s 50m -fusion -t -IL Dynamics -IL StdEnv -IL Platform main.

Вы можете получить версию TIO (и меня), используя ссылку в заголовке, или более свежую версию здесь .

Οurous
источник
Я не думаю, что этот код дает правильный вывод. Я попробовал это с s = 8, и это дает [9,86,126,130,130,130,130,130,130]
Anush
@ Ануш, хм, я знаю, что проверила. Я посмотрю, изменил ли я что-нибудь между этим и опубликованным, дай мне несколько часов, и я смогу сделать это в перерыве.
августа
@Anush Почему вы предоставляете s? В ответе на вопрос « Для данного n ваш код должен выводить ответ для всех значений s от 1 до 9».
Οurous
1
Я думаю, что это то, что вы называете «заморозкой мозгов» с моей стороны :) Сейчас я проверю ваш код.
Ануш