Created
June 4, 2025 15:17
-
-
Save eevmanu/229d81cd8886a4ca7a77a7e7bd63a2b0 to your computer and use it in GitHub Desktop.
2 scripts to decompose a number that is greater than 501, split into numbers between 501 to 999, close to hundreds first, later close to tenth, later close to digit, in blocks which sum is lower than 5000 in case the number to decompose is greater than 5000 , little algorithm for yape cambiar dolares since yape te da un tasa de cambio preferenci…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
""" | |
Number Decomposition Module | |
This module decomposes a given number into sub-numbers within a specified range, | |
optimizing for "roundness" (preference for multiples of 100, then 10). | |
Usage Examples: | |
python decompose_number.py 1234 | |
python decompose_number.py 5000 | |
python decompose_number.py 10000 | |
The module takes a single integer argument and decomposes it into sub-numbers | |
between MIN_SUB_VALUE (501) and MAX_SUB_VALUE (999), with optimization for | |
rounder numbers (multiples of 100 are preferred, then multiples of 10). | |
For numbers larger than CHUNK_THRESHOLD (5000), the output is grouped into | |
chunks to improve readability. | |
Constants: | |
MIN_SUB_VALUE (int): Minimum value for sub-numbers (501) | |
MAX_SUB_VALUE (int): Maximum value for sub-numbers (999) | |
CHUNK_THRESHOLD (int): Threshold for grouping output (5000) | |
Functions: | |
get_niceness(val): Scores a number based on its roundness | |
get_initial_decomposition(n): Decomposes number into valid sub-numbers | |
optimize_parts(parts_list_orig): Optimizes sub-numbers for roundness | |
decompose_number_main(n_total): Main decomposition function | |
test_decompose_number_main(n_total): Test function with predefined values | |
Example Output: | |
$ python decompose_number.py 1234 | |
Number: 1234 | |
Sub-numbers: [600, 634], Sum: 1234 | |
$ python decompose_number.py 6000 | |
Number: 6000 | |
Group 1: [900, 900, 900, 900, 900], Sum: 4500 | |
Group 2: [800, 700], Sum: 1500 | |
""" | |
import math | |
import sys | |
# --- Constants --- | |
MIN_SUB_VALUE = 501 | |
MAX_SUB_VALUE = 999 | |
CHUNK_THRESHOLD = 5000 # For grouping output if N_total > 5000 | |
# --- Helper Functions --- | |
def get_niceness(val): | |
""" | |
Scores a number based on its roundness. Higher score is better. | |
""" | |
if not (MIN_SUB_VALUE <= val <= MAX_SUB_VALUE): # Should not happen if logic is correct | |
return -1 # Invalid part | |
if val % 100 == 0: | |
return 3 # Multiple of 100 | |
elif val % 10 == 0: | |
return 2 # Multiple of 10 | |
else: | |
return 1 # Not particularly round | |
def get_initial_decomposition(n): | |
""" | |
Decomposes a number n into sub-numbers, each between MIN_SUB_VALUE and MAX_SUB_VALUE. | |
Returns a list of numbers or an error string. | |
""" | |
if not isinstance(n, int): | |
return "Input for initial decomposition must be an integer." | |
if n == 0: # No parts needed for zero | |
return [] | |
if n < MIN_SUB_VALUE: | |
return (f"Value {n} is too small to form any sub-number " | |
f"(min is {MIN_SUB_VALUE}). It must be 0 or >= {MIN_SUB_VALUE}.") | |
if MIN_SUB_VALUE <= n <= MAX_SUB_VALUE: | |
# If n itself is a valid single sub-number | |
return [n] | |
# Now, n > MAX_SUB_VALUE (or n requires multiple parts like 1002) | |
min_k = (n + MAX_SUB_VALUE - 1) // MAX_SUB_VALUE # ceil(n / MAX_SUB_VALUE) | |
max_k = n // MIN_SUB_VALUE # floor(n / MIN_SUB_VALUE) | |
if min_k > max_k or min_k == 0 : # min_k can be 0 if n is small, handled by earlier checks | |
# min_k > max_k handles cases like 1000, 1001 | |
return (f"Number {n} cannot be decomposed into sub-numbers between " | |
f"{MIN_SUB_VALUE} and {MAX_SUB_VALUE} (e.g. N=1000, 1001).") | |
k = min_k # Use min_k to get fewer, larger parts initially | |
base_value = n // k | |
remainder = n % k | |
sub_numbers = [] | |
for i in range(k): | |
current_part = base_value + 1 if i < remainder else base_value | |
sub_numbers.append(current_part) | |
# Verification | |
current_sum = sum(sub_numbers) | |
all_valid_range = all(MIN_SUB_VALUE <= val <= MAX_SUB_VALUE for val in sub_numbers) | |
if not (current_sum == n and all_valid_range): | |
# This should ideally not be hit if logic for k and distribution is correct | |
return (f"Internal error in initial decomposition for {n}. " | |
f"Parts: {sub_numbers}, Sum: {current_sum}, All valid: {all_valid_range}") | |
return sub_numbers | |
def optimize_parts(parts_list_orig): | |
""" | |
Optimizes the list of sub-numbers to prefer rounder numbers. | |
Iteratively adjusts pairs of numbers. | |
""" | |
parts = list(parts_list_orig) # Work on a copy | |
k = len(parts) | |
if k < 2: | |
return parts # Nothing to optimize with fewer than 2 parts | |
improved_in_outer_loop = True | |
max_outer_loops = k * k * 5 # Safety break for very complex scenarios, adjust as needed | |
outer_loop_count = 0 | |
while improved_in_outer_loop and outer_loop_count < max_outer_loops: | |
improved_in_outer_loop = False | |
outer_loop_count += 1 | |
for i in range(k): | |
for j in range(i + 1, k): | |
s_i, s_j = parts[i], parts[j] | |
original_pair_sum = s_i + s_j | |
current_best_niceness_sum = get_niceness(s_i) + get_niceness(s_j) | |
best_si_cand, best_sj_cand = s_i, s_j | |
# Potential candidates for s_i by trying to make it rounder | |
# Check values around s_i, then calculate required s_j | |
# Include s_i itself to ensure we don't worsen things if no better option | |
potential_si_targets = sorted(list(set([ | |
s_i, | |
(s_i // 100) * 100, # floor to hundred | |
((s_i + 99) // 100) * 100, # ceil to hundred | |
(s_i // 10) * 10, # floor to ten | |
((s_i + 9) // 10) * 10, # ceil to ten | |
]))) | |
for si_target in potential_si_targets: | |
sj_candidate = original_pair_sum - si_target | |
if MIN_SUB_VALUE <= si_target <= MAX_SUB_VALUE and \ | |
MIN_SUB_VALUE <= sj_candidate <= MAX_SUB_VALUE: | |
candidate_niceness_sum = get_niceness(si_target) + get_niceness(sj_candidate) | |
if candidate_niceness_sum > current_best_niceness_sum: | |
current_best_niceness_sum = candidate_niceness_sum | |
best_si_cand, best_sj_cand = si_target, sj_candidate | |
# Optional: Add tie-breaking if scores are equal. | |
# For now, only strictly better total niceness updates. | |
# Symmetrically, try to make s_j rounder and adjust s_i | |
potential_sj_targets = sorted(list(set([ | |
s_j, | |
(s_j // 100) * 100, | |
((s_j + 99) // 100) * 100, | |
(s_j // 10) * 10, | |
((s_j + 9) // 10) * 10, | |
]))) | |
for sj_target in potential_sj_targets: | |
si_candidate = original_pair_sum - sj_target | |
if MIN_SUB_VALUE <= sj_target <= MAX_SUB_VALUE and \ | |
MIN_SUB_VALUE <= si_candidate <= MAX_SUB_VALUE: | |
candidate_niceness_sum = get_niceness(si_candidate) + get_niceness(sj_target) | |
if candidate_niceness_sum > current_best_niceness_sum: | |
current_best_niceness_sum = candidate_niceness_sum | |
best_si_cand, best_sj_cand = si_candidate, sj_target | |
if best_si_cand != s_i or best_sj_cand != s_j: # If an improvement was found for this pair | |
parts[i] = best_si_cand | |
parts[j] = best_sj_cand | |
improved_in_outer_loop = True # Signal to re-iterate over all pairs in a new outer loop pass | |
if outer_loop_count == max_outer_loops: | |
print(f"Warning: Max iterations ({max_outer_loops}) reached during optimization for original parts: {parts_list_orig}. Result: {parts}") | |
return parts | |
# --- Main Function --- | |
def decompose_number_main(n_total): | |
""" | |
Main function to decompose N_total. | |
Handles N_total >= 1000. | |
Optimizes sub-numbers for roundness. | |
Groups output if N_total > CHUNK_THRESHOLD. | |
""" | |
# if not isinstance(n_total, int) or n_total < 1000: | |
# return "Input number must be an integer >= 1000." | |
if not isinstance(n_total, int) or n_total < 501: | |
return "Input number must be an integer >= 501." | |
# if not isinstance(n_total, int): | |
# return "Input number must be an integer." | |
# 1. Get the initial decomposition for the entire N_total | |
initial_parts_flat = get_initial_decomposition(n_total) | |
if isinstance(initial_parts_flat, str): # This is an error message | |
return initial_parts_flat | |
if not initial_parts_flat and n_total > 0 : # Should be caught by error string from get_initial_decomposition | |
return f"Number {n_total} could not be decomposed initially (e.g. 1000, 1001)." | |
# 2. Optimize the flat list of parts | |
optimized_parts_flat = optimize_parts(initial_parts_flat) | |
# 3. Group output if N_total > CHUNK_THRESHOLD | |
if n_total <= CHUNK_THRESHOLD: | |
return optimized_parts_flat # Return a single list | |
else: | |
# Group the optimized_parts_flat into blocks whose sum <= CHUNK_THRESHOLD | |
grouped_results = [] | |
current_block = [] | |
current_block_sum = 0 | |
for part_val in optimized_parts_flat: | |
# Add to current block if it's empty or adding the part doesn't exceed threshold | |
if not current_block or (current_block_sum + part_val <= CHUNK_THRESHOLD): | |
current_block.append(part_val) | |
current_block_sum += part_val | |
else: | |
# Current block is full (or would become too full), start a new one | |
grouped_results.append(current_block) | |
current_block = [part_val] # Start new block with current part | |
current_block_sum = part_val | |
if current_block: # Add the last remaining block | |
grouped_results.append(current_block) | |
return grouped_results # Return list of lists | |
def test_decompose_number_main(n_total): | |
""" | |
Test function for decompose_number_main with various input values. | |
This function tests the decompose_number_main function with a predefined set of | |
test numbers ranging from 500 to 5000. For each test number, it calls the | |
decompose_number_main function and displays the results including: | |
- The original number being decomposed | |
- The resulting sub-numbers (if successful decomposition) | |
- The sum of sub-numbers to verify correctness | |
- Whether all parts fall within the valid range [MIN_SUB_VALUE, MAX_SUB_VALUE] | |
The function includes breakpoints for debugging purposes and stores all results | |
in a dictionary for further analysis. | |
Args: | |
n_total: Total number parameter (appears to be unused in current implementation) | |
Returns: | |
None: This function prints results and doesn't return a value | |
Note: | |
This function assumes the existence of: | |
- decompose_number_main() function | |
- MIN_SUB_VALUE and MAX_SUB_VALUE constants | |
""" | |
# Let's test with some numbers | |
test_numbers = [500, 501, 502, 998, 999, 1000, 1001, 1002, 1500, 1998, 2000, 2500, 5000,] | |
results = {} | |
print("Decomposition Test Results:") | |
for num in test_numbers: | |
print(f" Number: {num}") | |
result = decompose_number_main(num) | |
results[num] = result | |
breakpoint() | |
if isinstance(result, list): | |
print(f"-> Sub-numbers: {result}, Sum: {sum(result)}") | |
all_parts_in_range = all(MIN_SUB_VALUE <= val <= MAX_SUB_VALUE for val in result) | |
print(f" All parts in [{MIN_SUB_VALUE}, {MAX_SUB_VALUE}]: {all_parts_in_range}") | |
else: | |
print(f"-> {result}") | |
breakpoint() | |
if __name__ == "__main__": | |
if len(sys.argv) > 1: | |
try: | |
input_number = int(sys.argv[1]) | |
result = decompose_number_main(input_number) | |
print(f"Number: {input_number}") | |
if isinstance(result, list): | |
if all(isinstance(item, list) for item in result): | |
# Grouped results | |
for i, group in enumerate(result): | |
print(f"Group {i+1}: {group}, Sum: {sum(group)}") | |
else: | |
# Single list | |
print(f"Sub-numbers: {result}, Sum: {sum(result)}") | |
else: | |
print(f"Error: {result}") | |
except ValueError: | |
print("Error: Please provide a valid integer as argument.") | |
except IndexError: | |
print("Error: Please provide a number as argument.") | |
else: | |
print("Usage: python3 decompose_number.py <number>") | |
# Run the test when script is executed directly | |
pass |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# Expected Script Purpose (based on filename): | |
# - Likely decomposes a number into its constituent parts (digits, factors, etc.) | |
# - May break down numbers into prime factors, place values, or mathematical components | |
# | |
# Example usage scenarios (inferred from filename): | |
# ./decompose_number.sh 123 # Decompose number into digits: 1, 2, 3 | |
# ./decompose_number.sh 24 # Find prime factors: 2^3 * 3 | |
# ./decompose_number.sh -p 100 # Decompose into prime factors | |
# ./decompose_number.sh -d 456 # Break into place values: 400 + 50 + 6 | |
# | |
# Edge cases to consider: | |
# - Negative numbers: ./decompose_number.sh -42 | |
# - Zero: ./decompose_number.sh 0 | |
# - Large numbers: ./decompose_number.sh 999999999 | |
# - Decimal numbers: ./decompose_number.sh 3.14159 | |
# - Invalid input: ./decompose_number.sh abc | |
# Split a single block (1000-4999) – greedy but honours the same preference | |
split_block() { | |
local target=$1 remain=$1 out=() | |
while (( remain )); do | |
# 1. try multiples of 100 | |
local cand=$(( (remain>999 ? 999 : remain) / 100 * 100 )) | |
while (( cand >= 501 )); do | |
(( remain -= cand )) | |
if (( remain == 0 || remain >= 501 )); then | |
out+=("$cand") | |
break | |
fi | |
(( remain += cand, cand -= 100 )) # back-off & retry | |
done | |
# 2. if step 1 failed, fall back to multiples of 10 | |
if (( cand < 501 )); then | |
cand=$(( (remain>999 ? 999 : remain) / 10 * 10 )) | |
while (( cand >= 501 )); do | |
(( remain -= cand )) | |
if (( remain == 0 || remain >= 501 )); then | |
out+=("$cand"); break | |
fi | |
(( remain += cand, cand -= 10 )) | |
done | |
fi | |
# 3. last resort: exact number (must be within range) | |
if (( cand < 501 )); then | |
if (( remain < 501 || remain > 999 )); then | |
echo "Impossible to split $target" >&2; return 1 | |
fi | |
out+=("$remain"); remain=0 | |
fi | |
done | |
printf '%s ' "${out[@]}"; echo | |
} | |
# Dispatcher for any input ≥1000 | |
split_number() { | |
local n=$1 | |
while (( n > 0 )); do | |
local block=$(( n > 4999 ? 4999 : n )) | |
if (( n - block > 0 && n - block < 1000 )); then | |
block=$(( block - (1000 - (n - block)) )) | |
fi | |
split_block "$block" || return 1 | |
n=$(( n - block )) | |
done | |
} | |
# ---- quick test ------------------------------------------------------------ | |
# for t in 500 501 502 998 999 1000 1001 1002 1300 2000 1501 4999 7321; do | |
# echo -n "$t → "; split_number "$t" | |
# done | |
# ---- quick test ------------------------------------------------------------ | |
# Main execution | |
if [[ $# -eq 0 ]]; then | |
echo "Usage: $0 <number1> [number2] [number3] ..." >&2 | |
exit 1 | |
fi | |
for num in "$@"; do | |
if [[ ! $num =~ ^[0-9]+$ ]]; then | |
echo "Error: '$num' is not a valid number" >&2 | |
exit 1 | |
fi | |
echo -n "$num → " | |
split_number "$num" | |
done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment