Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Use this tag for questions about code (to be) compiled with a C++ compiler.
В заголовке указано: на Си (С++), Objective C не подходит ни под одну из категорий?Конечно же, нет.
#include <type_traits>
#include <limits>
template <class K, class N, bool, bool>
struct bci_helper {
static constexpr int value = 1;
};
template <class T, T K, T N>
using bci = bci_helper<std::integral_constant<T,K>, std::integral_constant<T,N>, K!=N, K!=0>;
template <class T, T K, T N>
struct bci_helper<std::integral_constant<T,K>, std::integral_constant<T,N>, true, true> {
static_assert(bci<T, K, N-1>::value <= std::numeric_limits<T>::max() - bci<T, K-1, N-1>::value,"Integer overflow");
static constexpr T value = bci<T, K, N-1>::value + bci<T,K-1,N-1>::value;
};
#include <iostream>
#include <type_traits>
template <char ... Chars>
struct number {};
std::ostream& operator<< (std::ostream& os, number<>){
return os;
}
template <char C, char ... Chars>
std::ostream& operator<< (std::ostream& os, number<C, Chars...>){
os << C << number<Chars...>{};
return os;
}
template <class A, class B>
struct append;
template <char ... As, char ... Bs>
struct append<number<As...>, number<Bs...>> {
using type = number<As..., Bs...>;
};
template <class Source, class Result = number<>>
struct reverse;
template <char A, char ... As, char ... Bs>
struct reverse<number<A,As...>, number<Bs...>> {
using type = typename reverse<number<As...>,number<A,Bs...>>::type;
};
template <char ... Bs>
struct reverse<number<>,number<Bs...>> {
using type = number<Bs...>;
};
template <class T>
struct remove_zeroes {
using type = T;
};
template <>
struct remove_zeroes<number<'0'>> {
using type = number<'0'>;
};
template <char ... As>
struct remove_zeroes<number<'0',As...>> {
using type = typename remove_zeroes<number<As...>>::type;
};
template <char A, char B, char C>
struct basic_sum {
static constexpr char H = '0' + ((A-'0') + (B-'0') + (C -'0'))/10;
static constexpr char L = '0' + ((A-'0') + (B-'0') + (C -'0'))%10;
};
template <class A, class B, char C>
struct partial_sum;
template <char A, char ... As, char B, char ... Bs, char C>
struct partial_sum<number<A,As...>, number<B, Bs...>, C> {
using L = number<basic_sum<A,B,C>::L>;
using H = typename partial_sum<number<As...>,number<Bs...>,basic_sum<A,B,C>::H>::type;
using type = typename append<L,H>::type;
};
template <char A, char ... As, char C>
struct partial_sum<number<A,As...>,number<>,C> {
using L = number<basic_sum<A,'0',C>::L>;
using H = typename partial_sum<number<As...>,number<>,basic_sum<A,'0',C>::H>::type;
using type = typename append<L,H>::type;
};
template <char B, char ... Bs, char C>
struct partial_sum<number<>,number<B,Bs...>,C> {
using L = number<basic_sum<'0',B,C>::L>;
using H = typename partial_sum<number<>,number<Bs...>,basic_sum<B,'0',C>::H>::type;
using type = typename append<L,H>::type;
};
template <char C>
struct partial_sum<number<>,number<>,C> {
using L = number<C>;
using H = number<>;
using type = typename append<L,H>::type;
};
template <class A, class B>
using sum = typename remove_zeroes<typename reverse<typename partial_sum<typename reverse<A>::type,typename reverse<B>::type,'0'>::type>::type>::type;
template <class>
struct decrement_helper;
template <char A, char... As>
struct decrement_helper<number<A,As...>> {
using L = number<(A=='0')?'9':(A-1)>;
using H = typename std::conditional<A=='0',typename decrement_helper<number<As...>>::type,number<As...>>::type;
using type = typename append<L,H>::type;
};
template <>
struct decrement_helper<number<>> {
using type = number<>;
};
template <class T>
using decrement = typename remove_zeroes<typename reverse<typename decrement_helper<typename reverse<T>::type>::type>::type>::type;
template <class K, class N>
struct bcl_helper {
using type = sum<typename bcl_helper<K,decrement<N>>::type,typename bcl_helper<decrement<K>,decrement<N>>::type>;
};
template <class N>
struct bcl_helper<N,N> {
using type = number<'1'>;
};
template <class N>
struct bcl_helper<number<'0'>,N> {
using type = number<'1'>;
};
template <typename CharT, CharT ...String> auto operator "" _n() {
return number<String...>();
}
template <class K, class N>
auto bcl(K,N) {
return typename bcl_helper<K,N>::type();
}
int main(int /*argc*/, char** /*argv*/)
{
std::cout << bcl("50"_n,"100"_n) << std::endl;
}
unsigned bci2(int n, int k)
{
if (k>n/2) k=n-k; // возьмем минимальное из k, n-k.. В силу симметричность C(n,k)=C(n,n-k)
if (k==1) return n;
if (k==0) return 1;
unsigned r=1;
for (int i=1; i<=k;++i) {
if(r % i == 0)
{
r/=i;
r*=n-i+1;
}
else
{
r*=n-i+1;
r/=i;
}
}
return r;
}
Можно, конечно, в числителе и знаменмтеле поискать общие множители и посокращать их, но я не стал двигаиться в этом напралении.— См. обновление habrahabr.ru/post/274689/#comment_8729927
template<typename T>
T gcd(T a, T b)
{
while (b > 0)
{
a = a % b;
std::swap(a, b);
}
return a;
}
template <typename T>
T bci2(T n, T k)
{
if (k > n/2)
k = n - k;
if (k == 1)
return n;
if (k == 0)
return 1;
T r = 1;
for (T i = 1; i <= k; ++i) {
T f = gcd(r, i);
r /= f;
r *= n - i + 1;
r /= (i/f);
}
return r;
}
С unsigned int работает до 33, с unsigned long работает до 66.template <typename T>
T bci2(T n, T k)
{
if (k > n/2)
k = n - k;
if (k == 1)
return n;
if (k == 0)
return 1;
T r = 1;
for (T i = 1; i <= k; ++i) {
T a = n - i + 1;
T b = i;
// compute r = r * a / b;
T f = gcd(r, i);
r /= f;
b /= f;
T f1 = gcd(a, b);
r *= a/f1;
r /= b/f1;
}
return r;
}
Работает до 34 и 67 соответственно.template <typename T>
T bci2(T n, T k)
{
if (k > n/2)
k = n - k;
T r = 1;
for (T i = 1; i <= k; ++i)
{
// r * a / b = (r/b)*a + (r%b)*a/b
T a = n - i + 1;
r = (r/i)*a + ((r%i)*a)/i;
}
return r;
}
Естественно, пришлось использовать «длинную арифметику» (свою). Для этой задачи я написал «рекурсию с памятью»: вычисленные коэффициенты запоминались в массиве и экспоненциального роста времени расчета не было.Во-первых стоило сразу привести в статье длинную арифметику. Это грустно, когда оперируют с double при расчёте целочисленных значений
return ceil(r-0.2); // округлим до ближайшего целого, отбросив дробную частьА во-вторых, можно привести вычисление коэффициентов без факториалов, а динамически оперируя только суммой.
Дальнейшее повышение точности и расчет при n>67… используем double ...
Расхождение расчета bcd с точным значением начинается с n=57.Повысили точность?
Для экстремалов и «олимпийцев»и совершенно не раскрываете тему статьи для олимпийцев. И уберите кавычки с этого слова ради бога, олимпийцы это живые люди!
r=bcl(n-1,k); r+=+bcl(n-1,k-1);
«На олимпиады ссылаешься, а зачем они нужны? Пользы от них никакой нет.» Вопрос спорный. Не смог решить задачу, тебя не взяли в команду и обида гложет?
1. Для int и long long заметной глазу разницы в скорости нет (ну не замечает мой глаз миллисекунды).
2. Для олимпиадных задач, в которых надо было считать коэффициенты разницы тоже не было.
3. Код с запоминанием не такой простой и чуть более объемный (зачем искать/приводить путь в два шага, если есть путь в один шаг)
unsigned bcr(int n,int k)
{
if (k>n/2) k=n-k;
if (k==1) return n;
if (k==0) return 1;
return bcr(n-1,k)+bcr(n-1,k-1);
}
unsigned bcr_cache[N_MAX][K_MAX] = {0};
unsigned bcr(int n,int k)
{
if (k>n/2) k=n-k;
if (k==1) return n;
if (k==0) return 1;
if (bcr_cache[n][k] == 0)
bcr_cache[n][k] = bcr(n-1,k)+bcr(n-1,k-1);
return bcr_cache[n][k];
}
return ceil(r-0.2); // округлим до ближайшего целого, отбросив дробную часть
// typedef uint32_t bino_int;
// typedef uint64_t bino_int;
buffer[0] = 1;
for (size = 1; size <= n; ++size) {
bino_int prev = 1; // buffer[0]
for (size_t off = 1; off < size; ++off) {
bino_int tmp = buffer[off];
buffer[off] += prev;
prev = tmp;
}
buffer[size] = 1;
}
from timeit import timeit
def pow_binomial(n, k):
def eratosthenes(N): # Возвращает список простых чисел от 2 до N
simp = [2] # Начинаем с 2
nonsimp = set() # Составные числа. Пока пусто
for i in range(3, N + 1, 2): # Проходим все нечётные числа
if i not in nonsimp: # Если оно не составное, значит, новое простое
nonsimp |= {j for j in range(i * i, N + 1, 2 * i)} # Добавляем все кратные
simp.append(i) # А само число в список простых
return simp
def calc_pow_in_factorial(a, p): # Вычисляет кратность вхождения простого числа p в число a!
res = 0
while a:
a //= p
res += a
return res
# Не паримся и используем формулу n!/(k!(n-k)!)
simple = eratosthenes(n+1) # Берём список простых
n_fact_pows = {p: calc_pow_in_factorial(n, p) for p in simple}
k_fact_pows = {p: calc_pow_in_factorial(k, p) for p in simple}
nmk_fact_pows = {p: calc_pow_in_factorial(n - k, p) for p in simple}
ans = 1
for p in simple:
ans *= p ** (n_fact_pows[p] - k_fact_pows[p] - nmk_fact_pows[p])
return ans
def naive_factorial(n):
res = 1
for i in range(2, n + 1):
res *= i
return res
def naive_binomial(n, k):
return naive_factorial(n) // naive_factorial(k) // naive_factorial(n - k)
REPEATS = 10
N = 50000
K = 10000
setup = 'from __main__ import N, K, naive_binomial, pow_binomial'
print(timeit(stmt='pow_binomial(N, K)', setup=setup, number=REPEATS)/REPEATS)
print(timeit(stmt='naive_binomial(N, K)', setup=setup, number=REPEATS)/REPEATS)
>>>
0.02103983737743497
1.1220947057368764
def pow_binomial(n, k):
def calc_pow(a, p):
res = 0
while a:
a //= p
res += a
return res
ans = 1
for p in [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71]:
ans *= p ** (calc_pow(n, p) - calc_pow(k, p) - calc_pow(n - k, p))
return ans
Дальнейшее повышение точности и расчет при n>67
Для экстремалов и «олимпийцев»
Расчет биномиальных коэффициентов на Си (С++) и Python