forked from cool-RR/python_toolbox
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfactorials.py
More file actions
122 lines (92 loc) · 3.14 KB
/
factorials.py
File metadata and controls
122 lines (92 loc) · 3.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# Copyright 2009-2017 Ram Rachum.
# This program is distributed under the MIT license.
import math
import collections
import itertools
import numbers
infinity = float('inf')
infinities = (infinity, -infinity)
def factorial(x, start=1):
'''
Calculate a factorial.
This differs from the built-in `math.factorial` in that it allows a `start`
argument. If one is given, the function returns `(x!)/(start!)`.
Examples:
>>> factorial(5)
120
>>> factorial(5, 3)
60
'''
from python_toolbox import misc_tools
return misc_tools.general_product(range(start, x+1), start=1)
def inverse_factorial(number, round_up=True):
'''
Get the integer that the factorial of would be `number`.
If `number` isn't a factorial of an integer, the result will be rounded. By
default it'll be rounded up, but you can specify `round_up=False` to have
it be rounded down.
Examples:
>>> inverse_factorial(100)
5
>>> inverse_factorial(100, round_up=False)
4
'''
assert number >= 0
if number == 0:
return 0
elif number < 1:
return int(round_up) # Heh.
elif number == 1:
return 1
else:
current_number = 1
for multiplier in itertools.count(2):
current_number *= multiplier
if current_number == number:
return multiplier
elif current_number > number:
return multiplier if round_up else (multiplier - 1)
def from_factoradic(factoradic_number):
'''
Convert a factoradic representation to the number it's representing.
Read about factoradic numbers here:
https://en.wikipedia.org/wiki/Factorial_number_system
Example:
>>> from_factoradic((4, 0, 2, 0, 0))
100
'''
from python_toolbox import sequence_tools
assert isinstance(factoradic_number, collections.abc.Iterable)
factoradic_number = \
sequence_tools.ensure_iterable_is_sequence(factoradic_number)
number = 0
for i, value in enumerate(reversed(factoradic_number)):
assert 0 <= value <= i
number += value * math.factorial(i)
return number
def to_factoradic(number, n_digits_pad=0):
'''
Convert a number to factoradic representation (in a tuple.)
Read about factoradic numbers here:
https://en.wikipedia.org/wiki/Factorial_number_system
Example:
>>> to_factoradic(100)
(4, 0, 2, 0, 0)
Use `n_digits_pad` if you want to have the result padded with zeroes:
>>> to_factoradic(100, n_digits_pad=7)
(0, 0, 4, 0, 2, 0, 0)
'''
assert isinstance(number, numbers.Integral)
assert number >= 0
assert isinstance(n_digits_pad, numbers.Integral)
n_digits = inverse_factorial(number, round_up=False) + 1
digits = [None] * n_digits
current_number = number
for i in range(n_digits)[::-1]:
unit = math.factorial(i)
digits[n_digits - i - 1], current_number = divmod(current_number, unit)
result = tuple(digits)
if (len(result) < n_digits_pad):
return ((0,) * (n_digits_pad - len(result))) + result
else:
return result