Язык Си в примерах/Треугольник Паскаля

Материал из testwiki
Перейти к навигации Перейти к поиску

Шаблон:Содержание «Язык Си в примерах»

Всем известны простые формулы

  • (a+b)2=a2+2ab+b2
  • (a+b)3=a3+3a2b+3ab2+b3

А как находить коэффициенты в разложении (a+b)n?

Рассмотрим следующую таблицу:

То, что нарисовано справа, называется треугольником Паскаля — в n-й строчке этого треугольника находятся коэффициенты для разложения (a+b)n.

Число номер k+1 в n-й строчке называется биномиальным коэффициентом Cnk.

Например, C30=1,C52=10,C42=6.

Стороны треугольника Паскаля состоят из единичек. Каждое число внутри треугольника Паскаля равно сумме двух чисел, стоящих над ним справа и слева в предыдущей строчке:

Cn+1k+1=Cnk+1+Cnk.

Эти числа возникают в задаче о числе сочетаний: Cnk — это число способов выбрать k элементов из n различных. Например, число байт, в которых ровно 3 единицы — это число C83 — число способов выбрать три бита, в которых будут стоять единицы, из восьми бит байта.

Докажите, что
Cn1=n,Cnk+Cnk+1=Cn+1k+1,Cnk=n!k!(nk)!.

Рассмотрим две программы, которые решают следующие задачи:

  1. Запрограммировать функцию C(n,k)=Cnk.
  2. Вывести на экран n строчек треугольника Паскаля.
/*
   Вычисление биномиальных коэффициентов.
 */
#include <stdio.h>

long
C (long n, long k)
{
  if (k == 0 || n == k)
    return 1;
  return C (n - 1, k - 1) + C (n - 1, k);
}

int
main ()
{
  long n, k;
  scanf ("%ld%ld", &n, &k);
  printf ("%ld ", C (n, k));
  return 0;
}
  • Сколько раз вызовется функция C(., .) при вычислении С(n, k)?
  • Докажите, что время вычисления Cnk по приведенному алгоритму пропорционально Cnk.
  • Оцените ассимптотику Cnn/2, а именно, напишите программу, которая вычисляет logCnn/2 для n=2,4,...,40 и нарисуйте график

logCnn/2 от n.

/*
    Вычисление n-й строки треугольника Паскаля.
*/
#include <stdio.h>
#define N 1000

int
main ()
{
  long c[N];
  long n, i, j;
  scanf ("%ld", &n);
  for (i = 1; i <= n; i++)
    c[i] = 0;
  c[0] = 1;
  for (j = 1; j <= n; j++)
    for (i = j; i >= 1; i--)
      c[i] = c[i - 1] + c[i];
  for (i = 0; i <= n; i++)
    printf ("%ld ", c[i]);
  return 0;
}
  • Докажите, что указанный алгоритм вычисления n-й строчки треугольника Паскаля

работает быстрее, чем алгоритм вычисления Cnk из предыдущей программы, а именно время работы пропорционально n2.

  • Начиная с какого n самое большое число из n-й строчки треугольника Паскаля не умещается в тип long?

Еще один вариант реализации вычисления Биномиального коэффициента из общей формулы Cnk (без рекурсии):

/*
  Вычисление биномиальных коэффициентов
*/
#include <assert.h>
#include <stdio.h>

int
binomial (int row, int pos)
{
  int koef = 1;
  int i;
  for (i = pos + 1; i <= row; i++)
    koef = koef * i;
  for (i = 1; i < (row - pos + 1); i++)
    koef = koef / i;
  return koef;
}

int
main (void)
{
  int row, pos;
  int r = scanf ("%i%i", &row, &pos);
  assert (r == 2);
  printf ("%i", binomial (row, pos));
  return 0;
}

Если посмотреть на Треугольник Паскаля, то можно заметить симметрию. Если исходить из нее и общей формулы Cnk, то можно еще немного улучшить алгоритм (уменьшив число проходов) из кода выше сокращая числитель на больший элемент знаменателя:

/*
  Вычисление биномиальных коэффициентов
*/
#include <assert.h>
#include <stdio.h>

int
binomial (int row, int pos)
{
  int koef = 1;
  int i;
  if (row - pos > pos)
    pos = row - pos;
  for (i = pos + 1; i <= row; i++)
    koef = koef * i;
  for (i = 1; i < (row - pos + 1); i++)
    koef = koef / i;
  return koef;
}

int
main (void)
{
  int row, pos;
  int r = scanf ("%i%i", &row, &pos);
  assert (r == 2);
  printf ("%i", binomial (row, pos));
  return 0;
}

Стоит учитывать, что в реализованных примерах нет защиты от дурака (переполнение значения переменной).

Шаблон:BookCat