кількість рядків, коли кожен символ повинен виникати навіть рази


9

Я блукав черепом при цій проблемі вже деякий час, і це насправді починає мене засмучувати. Проблема полягає в наступному:

У мене є набір символів, A, B, C, і D. Я повинен сказати, якими способами може бути побудований рядок з цих символів, коли довжина є, nі кожен символ повинен виникати навіть раз.

Наприклад, відповідь n = 2- 4:

AA
BB
CC
DD

Відповідь n = 4- 40. Деякі з цих дійсних рядків:

AAAA
AABB
CACA
DAAD
BCCB

Я застряг, коли придумував логіку. Я відчуваю, що для цього може бути рішення DP. Наполегливо пробиваючись через це не вдається: кількість рішень швидко переростає у величезну кількість.

Я намагався малювати всілякі ідеї на папері, безрезультатно. Майже всі ці ідеї мені довелося відкинути, оскільки їх складність була надто великою. Рішення повинно бути ефективним для n = 10^4.

Одна з моїх ідей полягала в тому, щоб ніколи не відслідковувати фактичні рядки, а лише те, чи з’явився кожен персонаж парним чи непарним часом. Я не міг придумати спосіб застосувати цю логіку.

Хтось може мені допомогти?


3
Чи потрібно перераховувати рядки чи обчислювати кількість рядків? Якщо вам потрібна лише кількість рядків, ви можете скористатися комбінаторикою для прямого обчислення кількості.

@Snowman Потрібно лише кількість можливих рядків. Однак мені здається, що я б тут міг використати комбінаторику. Навіть якби був спосіб, я впевнений , що ця проблема не повинна бути вирішено з чистою математикою, і з цієї причини не хочу. Або що ви мали на увазі?
Олаві Мустанойя

2
Звичайно, ви можете використовувати комбінаторику. Для рядка довжиною N отримайте всі комбінації {AA, BB, CC, DD}. Для кожної комбінації отримайте унікальні перестановки. Потім об'єднайте результати для кожної комбінації в один набір унікальних перестановок. Я не впевнений, як це зробити, насамперед через обмеження унікальності, але впевнений, що є спосіб.

@Snowman Я бачу, що ти маєш на увазі. Але хіба це не вимагатиме зберігання принаймні комбінацій? Отримання кількості унікальних перестановок вимагає цього, і кількість комбінацій дуже швидко зростає до пропорцій, які я не міг би зберегти.
Олаві Мустанойя

1
Можливо. Я недостатньо добре розбираюся з комбінаторикою, щоб точно знати. Можливо, у Mathematics.SE є питання, схоже на це? У мене зараз немає часу копатися в ньому, але це цікава проблема. Я подумаю над цим і перевірте.

Відповіді:


5

Створіть f(n,d)як функцію, що дає кількість перестановок (парної) довжини, nвикористовуючи dрізні символи (тобто d=4у вашому випадку).

Зрозуміло, f(0,d) = 1і f(n,1) = 1як тільки одна композиція, якщо у вас є лише один символ або нульові пробіли.

Тепер крок індукції:

Щоб створити дійсний рядок за допомогою dсимволів, візьміть будь-яку коротшу рівну довжину за допомогою d-1символів та доведіть її до довжини, додавши ще один кратний цей новий символ. Кількість розташувань полягає в choose(n,n_newdigits)тому, що ви можете вибрати n_newdigitмісця з загальної довжини рядка, щоб мати нову цифру, а решту заповнити початковим рядком по порядку.

Щоб реалізувати це за допомогою наївної рекурсії в R, я зробив:

f <- function(n,d)
{
  if(n==0) return(1)
  if(d==1) return(1)
  retval=0
  for (i in seq(from=0, to=n, by=2)) retval=retval+f(n-i,d-1)*choose(n,i)
  return(retval)
}

f(4,4)
# 40    

f(500,4)
# 1.339386e+300 takes about 10 secs

для типу номерів, які вас цікавлять, я б подумав, що було б більш ефективно зберігати номери у двовимірному масиві та повторювати над збільшенням d, але це може залежати від вашого вибору мови.


4

Відповідь Міффа, безумовно, елегантна. Так як у мене шахта майже не закінчилася, я все-таки надаю її. Хороша річ, що я отримую однаковий результат для n = 500 :-)

Нехай d - кількість різних символів, дозволених, d = 4 у вашому випадку.

Нехай n - довжина рядка, зрештою, ви будете дивитися на рівні n.

Нехай u - кількість непарних символів у рядку.

Нехай N (n, d, u) - кількість рядків довжиною n, побудованих з d різних символів і мають u непарних символів. Давайте спробуємо обчислити N.

Існує досить багато важливих випадків:

u> d або u> n => N = 0

u <0 => N = 0

n% 2! = u% 2 => N = 0.

При переході від n до n + 1 u повинно або збільшитися на 1, або зменшити на 1, тому у нас є рекурсія відповідно до

N (n, d, u) = f (N (n-1, d, u-1), N (n-1, d, u + 1))

Скільки існує способів зменшити u на один. Це легко, тому що ми повинні з'єднати один з u непарних символів, що робить його просто u. Отже, 2-а частина f буде читати (u + 1) * N (n-1, d, u + 1), з попередженням, звичайно, що ми повинні дотримуватися, що N = 0, якщо u + 1> n-1 або u +1> d.

Після того, як ми зрозуміли це, легко зрозуміти, що таке перша частина f: на скільки способів ми можемо збільшити u, коли є неспарені символи u-1. Ну, ми повинні вибрати один із (k- (u-1)) символів, які є парними.

Отже, враховуючи всі кутові випадки, рекурсивна формула для N є

N (n, d, u) = (d- (u-1)) * N (n-1, d, u-1) + (u + 1) * N (n-1, d, u + 1)

Я не збираюся читати в http://en.wikipedia.org/wiki/Concrete_Mathematics, як вирішити рекурсію.

Натомість я написав код Java. Знову трохи незграбніше, як і в будь-якому випадку Java через свою багатослів’я. Але в мене була мотивація не використовувати рекурсії, оскільки вона перервана далеко-рано, принаймні на Яві, коли стек переповнюється на рівні 500 або 1000 вкладення.

Результат при n = 500, d = 4 і u = 0 дорівнює:

N (500, 4, 0) = 1339385758982834151185531311325002263201756014631917009304687985462938813906170153116497973519619822659493341146941433531483931607115392554498072196838958545795769042788035468026048125208904713757765805163872455056995809556627183222337328039422584942896842901774597806462162357229520744881314972303360

обчислюється за 0,2 секунди, завдяки запам'ятовуванню проміжних результатів. N (40000,4,0) обчислює менше ніж за 5 секунд. Код також тут: http://ideone.com/KvB5Jv

import java.math.BigInteger;

public class EvenPairedString2 {
  private final int nChars;  // d above, number of different chars to use
  private int count = 0;
  private Map<Task,BigInteger> memo = new HashMap<>();

  public EvenPairedString2(int nChars) {
    this.nChars = nChars;
  }
  /*+******************************************************************/
  // encodes for a fixed d the task to compute N(strlen,d,unpaired).  
  private static class Task {
    public final int strlen;
    public final int unpaired;

    Task(int strlen, int unpaired) {
      this.strlen = strlen;
      this.unpaired = unpaired;
    }
    @Override
    public int hashCode() {
      return strlen*117 ^ unpaired;
    }
    @Override
    public boolean equals(Object other) {
      if (!(other instanceof Task)) {
        return false;
      }
      Task t2 = (Task)other;
      return strlen==t2.strlen && unpaired==t2.unpaired;
    }
    @Override
    public String toString() {
      return "("+strlen+","+unpaired+")";
    }
  }
  /*+******************************************************************/
  // return corner case or memorized result or null  
  private BigInteger getMemoed(Task t) {
    if (t.strlen==0 || t.unpaired<0 || t.unpaired>t.strlen || t.unpaired>nChars
        || t.strlen%2 != t.unpaired%2) {
      return BigInteger.valueOf(0);
    }

    if (t.strlen==1) {
      return BigInteger.valueOf(nChars);
    }
    return memo.get(t);
  }

  public int getCount() {
    return count;
  }

  public BigInteger computeNDeep(Task t) {
    List<Task> stack = new ArrayList<Task>();
    BigInteger result = null;
    stack.add(t);

    while (stack.size()>0) {
      count += 1;
      t = stack.remove(stack.size()-1);
      result = getMemoed(t);
      if (result!=null) {
        continue;
      }

      Task t1 = new Task(t.strlen-1, t.unpaired+1);
      BigInteger r1 = getMemoed(t1);
      Task t2 = new Task(t.strlen-1, t.unpaired-1);
      BigInteger r2 = getMemoed(t2);
      if (r1==null) {
        stack.add(t);
        stack.add(t1);
        if (r2==null) {
          stack.add(t2);
        }
        continue;
      }
      if (r2==null) {
        stack.add(t);
        stack.add(t2);
        continue;
      }
      result = compute(t1.unpaired, r1, nChars-t2.unpaired, r2);
      memo.put(t,  result);
    }
    return result;
  }
  private BigInteger compute(int u1, BigInteger r1, int u2, BigInteger r2) {
    r1 = r1.multiply(BigInteger.valueOf(u1));
    r2 = r2.multiply(BigInteger.valueOf(u2));
    return r1.add(r2);
  }
  public static void main(String[] argv) {
    int strlen = Integer.parseInt(argv[0]);
    int nChars = Integer.parseInt(argv[1]);

    EvenPairedString2 eps = new EvenPairedString2(nChars);

    BigInteger result = eps.computeNDeep(new Task(strlen, 0));
    System.out.printf("%d: N(%d, %d, 0) = %d%n", 
                      eps.getCount(), strlen, nChars, 
                      result); 
  }
}

2

Я спробував придумати рішення, але не вдався і задав те саме питання щодо Mathematics.StackExchange . Завдяки Rus May , ось рішення Common Lisp:

(defun solve (n)
  (if (evenp n)
      (/ (+ (expt 4 n) (* 4 (expt 2 n))) 8)
      0))

Це завжди повертає 0 для непарних значень n. Бо n = 500ось висновок із SBCL :

* (time (solve 500))

    Evaluation took:
      0.000 seconds of real time
      0.000000 seconds of total run time (0.000000 user, 0.000000 system)
      100.00% CPU
      51,100 processor cycles
      0 bytes consed

    1339385758982834151185531311325002263201756014631917009304687985462938813906170153116497973519619822659493341146941433531483931607115392554498072196838958545795769042788035468026048125208904713757765805163872455056995809556627183222337328039422584942896842901774597806462162357229520744881314972303360
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.