From fc754bc2c4ee0fb086932acddd6d6254cbb1159b Mon Sep 17 00:00:00 2001 From: panariga Date: Tue, 10 Mar 2026 16:15:14 +0200 Subject: [PATCH] Add coins.c --- coins.c | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 coins.c diff --git a/coins.c b/coins.c new file mode 100644 index 0000000..3a9d903 --- /dev/null +++ b/coins.c @@ -0,0 +1,184 @@ +#include +#include +#include +#include +#include + +#define MAX_N 1024 +#define WORDS ((MAX_N / 64) + 1) +#define ITERATIONS 10000000 +#define NUMBER 86 + +// ========================================== +// 1. TRADITIONAL DP (FOR-LOOP) APPROACH +// ========================================== +int min_coins_dp(int n, int* coins, int num_coins) { + int dp[MAX_N + 1]; + dp[0] = 0; + + for (int i = 1; i <= n; i++) { + dp[i] = 1000000; // Represent "infinity" + for (int c = 0; c < num_coins; c++) { + if (i >= coins[c]) { + if (dp[i - coins[c]] + 1 < dp[i]) { + dp[i] = dp[i - coins[c]] + 1; + } + } + } + } + return dp[n] == 1000000 ? -1 : dp[n]; +} + +// ========================================== +// 2. NATIVE BITWISE APPROACH +// ========================================== +void shift_left_and_or(uint64_t* dest, const uint64_t* src, int shift) { + int word_shift = shift / 64; + int bit_shift = shift % 64; + + for (int i = WORDS - 1; i >= word_shift; i--) { + int src_idx = i - word_shift; + uint64_t val = src[src_idx] << bit_shift; + + if (bit_shift > 0 && src_idx > 0) { + val |= (src[src_idx - 1] >> (64 - bit_shift)); + } + dest[i] |= val; + } +} + +int min_coins_bitwise(int n, int* coins, int num_coins) { + if (n == 0) return 0; + if (n > MAX_N) return -1; + + uint64_t state[WORDS] = {0}; + state[0] = 1ULL; + uint64_t next_state[WORDS]; + + int steps = 0; + int target_word = n / 64; + int target_bit = n % 64; + uint64_t target_mask = 1ULL << target_bit; + + while (1) { + steps++; + memset(next_state, 0, sizeof(next_state)); + + for (int c = 0; c < num_coins; c++) { + shift_left_and_or(next_state, state, coins[c]); + } + + if (next_state[target_word] & target_mask) { + return steps; + } + + memcpy(state, next_state, sizeof(state)); + } +} + +// ========================================== +// 2. HIGH-PERFORMANCE BITWISE APPROACH +// ========================================== +int min_coins_bitwise_optimized(int n, int* coins, int num_coins) { + if (n == 0) return 0; + if (n > MAX_N) return -1; + + // How many 64-bit blocks do we ACTUALLY need for this 'n'? + int active_words = (n / 64) + 1; + + // Allocate two arrays to act as our "Current" and "Next" states + uint64_t bufferA[WORDS] = {0}; + uint64_t bufferB[WORDS] = {0}; + + bufferA[0] = 1ULL; + + // We will use pointers so we never have to use memcpy()! + uint64_t* state = bufferA; + uint64_t* next_state = bufferB; + + int steps = 0; + int target_word = n / 64; + uint64_t target_mask = 1ULL << (n % 64); + + while (1) { + steps++; + + // Manual clearing is faster than memset because we ONLY clear active_words + for (int i = 0; i < active_words; i++) { + next_state[i] = 0; + } + + // Apply coins using inline logic for maximum speed + for (int c = 0; c < num_coins; c++) { + int shift = coins[c]; + int w_shift = shift / 64; + int b_shift = shift % 64; + + // Only loop up to active_words! No useless math. + for (int i = active_words - 1; i >= w_shift; i--) { + int src_idx = i - w_shift; + uint64_t val = state[src_idx] << b_shift; + + if (b_shift > 0 && src_idx > 0) { + val |= (state[src_idx - 1] >> (64 - b_shift)); + } + next_state[i] |= val; + } + } + + // Did we hit the target? + if (next_state[target_word] & target_mask) { + return steps; + } + + // SWAP POINTERS! (Takes 0.000000001 seconds, completely eliminates memcpy) + uint64_t* temp = state; + state = next_state; + next_state = temp; + } +} + +// ========================================== +// MAIN BENCHMARK +// ========================================== +int main() { + int ukraine_coins[] = {1, 2, 5, 10, 25, 50}; + int num_coins = 6; + int n = NUMBER; // 868 cents (Answer: 17 coins + 10 + 5 + 2 + 1 = 21 coins) + + printf("Evaluating N = %d over %d iterations...\n\n", n, ITERATIONS); + + clock_t start, end; + double cpu_time_used; + volatile int total_sum = 0; // volatile prevents compiler loop-deletion + + // --- Benchmark DP --- + start = clock(); + for (int i = 0; i < ITERATIONS; i++) { + total_sum += min_coins_dp(n, ukraine_coins, num_coins); + } + end = clock(); + cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC; + printf("1. Traditional DP Time: %f seconds (Answer: %d)\n", cpu_time_used, total_sum / ITERATIONS); + + // --- Benchmark Bitwise --- + total_sum = 0; // Reset + start = clock(); + for (int i = 0; i < ITERATIONS; i++) { + total_sum += min_coins_bitwise(n, ukraine_coins, num_coins); + } + end = clock(); + cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC; + printf("2. Native Bitwise Time: %f seconds (Answer: %d)\n", cpu_time_used, total_sum / ITERATIONS); + + // --- Benchmark Bitwise Optimized--- + total_sum = 0; // Reset + start = clock(); + for (int i = 0; i < ITERATIONS; i++) { + total_sum += min_coins_bitwise_optimized(n, ukraine_coins, num_coins); + } + end = clock(); + cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC; + printf("3. Optimized Bitwise Time: %f seconds (Answer: %d)\n", cpu_time_used, total_sum / ITERATIONS); + return 0; +} \ No newline at end of file