diff --git a/DIRECTORY.md b/DIRECTORY.md index d6ceb04a62eb..9595cd85bc3f 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -62,6 +62,7 @@ * [Generate Parentheses Iterative](backtracking/generate_parentheses_iterative.py) * [Hamiltonian Cycle](backtracking/hamiltonian_cycle.py) * [Knight Tour](backtracking/knight_tour.py) + * [M Coloring Problem](backtracking/m_coloring_problem.py) * [Match Word Pattern](backtracking/match_word_pattern.py) * [Minimax](backtracking/minimax.py) * [N Queens](backtracking/n_queens.py) @@ -97,6 +98,7 @@ * [Is Power Of Two](bit_manipulation/is_power_of_two.py) * [Largest Pow Of Two Le Num](bit_manipulation/largest_pow_of_two_le_num.py) * [Missing Number](bit_manipulation/missing_number.py) + * [Multibit Manipulation](bit_manipulation/multibit_manipulation.py) * [Next Power Of Two](bit_manipulation/next_power_of_two.py) * [Numbers Different Signs](bit_manipulation/numbers_different_signs.py) * [Parity](bit_manipulation/parity.py) @@ -108,6 +110,9 @@ ## [Blockchain](blockchain) * [Diophantine Equation](blockchain/diophantine_equation.py) + * [Merkle Tree](blockchain/merkle_tree.py) + * [Simple Blockchain](blockchain/simple_blockchain.py) + * [Simple Proof Of Work](blockchain/simple_proof_of_work.py) ## [Boolean Algebra](boolean_algebra) * [And Gate](boolean_algebra/and_gate.py) @@ -166,6 +171,7 @@ * [Porta Cipher](ciphers/porta_cipher.py) * [Rabin Miller](ciphers/rabin_miller.py) * [Rail Fence Cipher](ciphers/rail_fence_cipher.py) + * [Rc4](ciphers/rc4.py) * [Rot13](ciphers/rot13.py) * [Rsa Cipher](ciphers/rsa_cipher.py) * [Rsa Factorization](ciphers/rsa_factorization.py) @@ -180,6 +186,7 @@ * [Vernam Cipher](ciphers/vernam_cipher.py) * [Vigenere Cipher](ciphers/vigenere_cipher.py) * [Xor Cipher](ciphers/xor_cipher.py) + * [Xtea](ciphers/xtea.py) ## [Computer Vision](computer_vision) * [Cnn Classification](computer_vision/cnn_classification.py) @@ -198,6 +205,9 @@ ## [Conversions](conversions) * [Astronomical Length Scale Conversion](conversions/astronomical_length_scale_conversion.py) * [Binary To Decimal](conversions/binary_to_decimal.py) + * [Binary To Excess3](conversions/binary_to_excess3.py) + * [Binary To Gray](conversions/binary_to_gray.py) + * [Binary To Gray Code](conversions/binary_to_gray_code.py) * [Binary To Hexadecimal](conversions/binary_to_hexadecimal.py) * [Binary To Octal](conversions/binary_to_octal.py) * [Convert Number To Words](conversions/convert_number_to_words.py) @@ -205,6 +215,7 @@ * [Decimal To Binary](conversions/decimal_to_binary.py) * [Decimal To Hexadecimal](conversions/decimal_to_hexadecimal.py) * [Decimal To Octal](conversions/decimal_to_octal.py) + * [Endianness](conversions/endianness.py) * [Energy Conversions](conversions/energy_conversions.py) * [Excel Title To Column](conversions/excel_title_to_column.py) * [Hex To Bin](conversions/hex_to_bin.py) @@ -689,6 +700,7 @@ * [Astar](machine_learning/astar.py) * [Automatic Differentiation](machine_learning/automatic_differentiation.py) * [Data Transformations](machine_learning/data_transformations.py) + * [Dbscan](machine_learning/dbscan.py) * [Decision Tree](machine_learning/decision_tree.py) * [Dimensionality Reduction](machine_learning/dimensionality_reduction.py) * [Federated Averaging](machine_learning/federated_averaging.py) @@ -712,15 +724,18 @@ * [Loss Functions](machine_learning/loss_functions.py) * Lstm * [Lstm Prediction](machine_learning/lstm/lstm_prediction.py) + * [Mean Shift](machine_learning/mean_shift.py) * [Mfcc](machine_learning/mfcc.py) * [Mini Batch Gradient Descent](machine_learning/mini_batch_gradient_descent.py) * [Multilayer Perceptron Classifier](machine_learning/multilayer_perceptron_classifier.py) + * [Naive Bayes Text Classification](machine_learning/naive_bayes_text_classification.py) * [Ordinary Least Squares Regression](machine_learning/ordinary_least_squares_regression.py) * [Polynomial Regression](machine_learning/polynomial_regression.py) * [Principle Component Analysis](machine_learning/principle_component_analysis.py) * [Q Learning](machine_learning/q_learning.py) * [Random Forest Classifier](machine_learning/random_forest_classifier.py) * [Random Forest Regressor](machine_learning/random_forest_regressor.py) + * [Rmsprop](machine_learning/rmsprop.py) * [Scoring Functions](machine_learning/scoring_functions.py) * [Self Organizing Map](machine_learning/self_organizing_map.py) * [Sequential Minimum Optimization](machine_learning/sequential_minimum_optimization.py) @@ -737,6 +752,7 @@ * [Arc Length](maths/arc_length.py) * [Area](maths/area.py) * [Area Under Curve](maths/area_under_curve.py) + * [Autocorrelation](maths/autocorrelation.py) * [Average Absolute Deviation](maths/average_absolute_deviation.py) * [Average Mean](maths/average_mean.py) * [Average Median](maths/average_median.py) @@ -782,6 +798,7 @@ * [Fibonacci](maths/fibonacci.py) * [Find Max](maths/find_max.py) * [Find Min](maths/find_min.py) + * [First Fundamental Form](maths/first_fundamental_form.py) * [Floor](maths/floor.py) * [Gamma](maths/gamma.py) * [Gaussian](maths/gaussian.py) @@ -844,6 +861,7 @@ * [Square Root](maths/numerical_analysis/square_root.py) * [Weierstrass Method](maths/numerical_analysis/weierstrass_method.py) * [Odd Sieve](maths/odd_sieve.py) + * [Padovan Sequence](maths/padovan_sequence.py) * [Pell Number](maths/pell_number.py) * [Perfect Cube](maths/perfect_cube.py) * [Perfect Number](maths/perfect_number.py) @@ -873,6 +891,8 @@ * [Reverse Factorial Recursive](maths/reverse_factorial_recursive.py) * [Segmented Sieve](maths/segmented_sieve.py) * Series + * [Alternate Harmonic Series](maths/series/alternate_harmonic_series.py) + * [Alternating Harmonic Series](maths/series/alternating_harmonic_series.py) * [Arithmetic](maths/series/arithmetic.py) * [Geometric](maths/series/geometric.py) * [Geometric Series](maths/series/geometric_series.py) @@ -912,6 +932,7 @@ * [Polygonal Numbers](maths/special_numbers/polygonal_numbers.py) * [Pronic Number](maths/special_numbers/pronic_number.py) * [Proth Number](maths/special_numbers/proth_number.py) + * [Spy Number](maths/special_numbers/spy_number.py) * [Triangular Numbers](maths/special_numbers/triangular_numbers.py) * [Trimorphic Number](maths/special_numbers/trimorphic_number.py) * [Ugly Numbers](maths/special_numbers/ugly_numbers.py) diff --git a/bit_manipulation/multibit_manipulation.py b/bit_manipulation/multibit_manipulation.py new file mode 100644 index 000000000000..67bcc96e0dd8 --- /dev/null +++ b/bit_manipulation/multibit_manipulation.py @@ -0,0 +1,254 @@ +"""Bit integer manipulation, both single bit and multi-bit list-like + slicing functions ( get, set, insert, remove ) implemented with + builtin bitwise operations. + +See: +https://high-python-ext-3-algorithms.readthedocs.io/ko/latest/chapter5.html#insert-bit +https://en.wikipedia.org/wiki/Bit_manipulation#Bit_manipulation_operations +https://github.com/billbreit/BitWiseApps + +All parameters must be must be int >= 0, referred to as a 'bit integer'. + + bint:int + The bit integer to be accessed or returned as modified. + + index:int + The offset into the bit position from right, + 0b010111 -> list [1,1,1,0,1,0]. big-endian -> little-endian + For inserts, index is the position to the right of index, + index 0 -> right of rightmost bit. + For gets, sets and removes, it is the position of the bit itself. + + value:int + Either [0,1] for single bit, or int value for multibit, + bit_length(value) <= bitlen. + + bitlen:int + The effective mask length, spec. leading zeros + ( bitlen 4 value 1 -> 0001 ) + +The bitwise expressions may look convoluted, but basically, there are +just three parts: left-hand side, value, right-hand side. + +For example, say you want to insert two ones in the middle of 0b101101, +that is -> 0b10111101. Index is 3 ( 0 ,1, 2, 3 from the right ) and the +value is 3 (0b11) with a bit length of 2. + +- Shift >> index right to produce 0b101 +- Shift left << bit length to produce 0b10100. +- OR in the ones producing 0b10111. +- Left shift << index producing 0b10111000. +- Using a bit mask (1 << index)-1 -> 0b111, AND with the original bint, + ( 0b101101 & 0b111 ) -> 0b101. +- OR that into the working 0b10111000, that is, ( 0b10111000 | 0b101 ) + -> 0b10111101. + +To remove the center two bits of 0b101101 -> 0b1001, the process is mostly +the same. Index is 2 for the remove operation on the right-center bit +rather than 3 for inserting, because we are referring to the bit itself +rather the position to the right of the bit index. + +- The initial right shift is index (2 ) + bit_length(2), taking out the two + middle bits and producing 0b10. +- The left shift of index produces 0b1000. +- The original bint is ANDed with bitmask 0b11 producing 0b01 which is + ORed with 0b1000 yielding the target 0b1001. + +Bit manipulation operations can be tricky to debug. In the insert example +above, the result of inserting 0b11 in the center ( index=3 ) or to the +right ( index=2 ) produces the same correct result despite the unintended +misspecification. Why is it working sometimes and not others ? Frequently, +it's the result of inserting at the wrong index, for the hundredth time ! + +Various bit insert/remove solutions exist using bin() string functions +and slicing, but this bitwise implementation is significantly faster +(about 3x) on Python for big ints (2^100). + +See https://github.com/billbreit/BitWiseApps/blob/main/dev/time_ops.py + +""" + +bit_length = int.bit_length + + +def bit_get(bint: int, index: int) -> int: + """Get value of bit at index in bint. + + >>> bit_get(15, 0) + 1 + >>> bit_get(15, 4) + 0 + >>> bit_get(0, 4) + 0 + >>> bit_get(-1, 2) + Traceback (most recent call last): + ... + ValueError: All input values must be positive integers. + >>> bit_get(0, -1) + Traceback (most recent call last): + ... + ValueError: All input values must be positive integers. + """ + + return multibit_get(bint, index, 1) + + +def bit_set(bint: int, index: int, value: int = 1) -> int: + """Set bit at index to value 1 or 0, like set() or unset(). + + >>> bit_set(15, 0, 0) + 14 + >>> bit_set(15, 4, 1) + 31 + >>> bit_set(31, 6, 0) + 31 + >>> bit_set(31, 6, 3) + Traceback (most recent call last): + ... + ValueError: Input value must be 1 or 0. + """ + + if value not in [0, 1]: + raise ValueError("Input value must be 1 or 0.") + + return multibit_set(bint, index, 1, value) + + +def bit_insert(bint: int, index: int, value: int = 1) -> int: + """Insert bit value before index. + + >>> bit_insert(15, 0, 0) + 30 + >>> bit_insert(15, 0, 1) + 31 + >>> bit_insert(15, 4, 1) + 31 + >>> bit_insert(31, 6, 0) + 31 + """ + + if value not in [0, 1]: + raise ValueError("Input value must be 1 or 0.") + + return multibit_insert(bint, index, 1, value) + + +def bit_remove(bint: int, index: int) -> int: + """Remove the bit at index from bint. + + >>> bit_remove(15, 0) + 7 + >>> bit_remove(15, 1) + 7 + >>> bit_remove(31, 4) + 15 + >>> bit_remove(31, 6) + 31 + """ + + return multibit_remove(bint, index, 1) + + +def multibit_get(bint: int, index: int, bit_len: int) -> int: + """Get bit_len number of bits starting from index. + 819 = 1100110011. + + >>> multibit_get(0, 1, 1) + 0 + >>> multibit_get(15, 0, 3) + 7 + >>> multibit_get(819, 2, 4) + 12 + >>> multibit_get(819, 4, 6) + 51 + """ + + if bint < 0 or index < 0 or bit_len < 0: + raise ValueError("All input values must be positive integers.") + + return (bint >> index) & ((1 << bit_len) - 1) + + +def multibit_set(bint: int, index: int, bit_len: int, value: int) -> int: + """Overlay bint at index with value for bit_len bits. + + >>> multibit_set(0, 1, 1, 0) + 0 + >>> multibit_set(15, 0, 2, 0) + 12 + >>> multibit_set(22, 0, 1, 1) + 23 + >>> multibit_set(22, 2, 1, 0) + 18 + >>> multibit_set(22, 2, 1, 3) + Traceback (most recent call last): + ... + ValueError: Bit length of value can not be greater than specified bit length. + """ + + if bint < 0 or index < 0 or bit_len < 0 or value < 0: + raise ValueError("All input values must be positive integers.") + if bit_length(value) > bit_len: + raise ValueError( + "Bit length of value can not be greater than specified bit length." + ) + + return ((((bint >> (index + bit_len)) << bit_len) | value) << index) | ( + bint & (1 << index) - 1 + ) + + +def multibit_insert(bint: int, index: int, bit_len: int, value: int) -> int: + """Insert value before index-th slot + + >>> multibit_insert(0, 1, 1, 1) + 2 + >>> multibit_insert(15, 1, 2, 0) + 57 + >>> multibit_insert(22, 0, 1, 1) + 45 + >>> multibit_insert(22, 2, 1, 0) + 42 + >>> multibit_insert(22, 2, 0, 0) + 22 + >>> multibit_insert(22, 2, 1, 3) + Traceback (most recent call last): + ... + ValueError: Bit length of value can not be greater than specified bit length. + """ + + if bint < 0 or index < 0 or bit_len < 0 or value < 0: + raise ValueError("All input values must be positive integers.") + if bit_length(value) > bit_len: + raise ValueError( + "Bit length of value can not be greater than specified bit length." + ) + + return ((((bint >> index) << bit_len) | value) << index) | bint & ((1 << index) - 1) + + +def multibit_remove(bint: int, index: int, bit_len: int) -> int: + """Remove bits in bint from index to index+bit_len. + + >>> multibit_remove(3, 1, 1) + 1 + >>> multibit_remove(15, 1, 2) + 3 + >>> multibit_remove(22, 0, 1) + 11 + >>> multibit_remove(22, 2, 2) + 6 + >>> multibit_remove(22, 2, 6) + 2 + """ + + if bint < 0 or index < 0 or bit_len < 0: + raise ValueError("All input values must be positive integers.") + + return ((bint >> index + bit_len) << index) | bint & ((1 << index) - 1) + + +if __name__ == "__main__": + import doctest + + doctest.testmod()