THE SYLLABUS TASK
What you need to solve
Implement the Multiplication Method hash function.
Nonnegative key (0–1,000,000), then table size m (1–1000). A = 0.6180339887498949. Compile locally with -lm when your toolchain requires the math library.
UNDERSTAND THE IDEA
Explanation
The multiplication method takes the fractional part of k×A, scales it by the table size and rounds down: h(k) = floor(m×frac(kA)).
Fractional part is computed as product−floor(product). Double precision replaces the manual’s float approximation and gives repeatable samples for the stated constant and accepted range.
This is a practical floating-point demonstration, not arbitrary-precision real arithmetic. The chosen constant and numeric representation are part of the implementation.
TURN THE IDEA INTO STEPS
Step-by-step algorithm
- Validate key and size.
- Multiply the key by the stated constant A.
- Subtract floor(product) to retain the fraction.
- Multiply the fraction by size and apply floor.
- Keep the final index within the accepted table range.
SEE THE CONTROL FLOW
Flowcharts
Diamonds show decisions; labeled arrows select a branch. Returning arrows repeat work. Helper-function steps group the operations explained above and in the code. These charts focus on the named core operation. On a phone, scroll within the diagram or open the full-size version.
Compute one hash index
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
/* Read one entire token; reject malformed, oversized and out-of-range input. */
int readToken(char text[], size_t capacity) {
int ch; size_t used = 0; int fits = 1;
while ((ch = getchar()) != EOF && isspace((unsigned char)ch)) { }
if (ch == EOF) return 0;
do {
if (ch == 0) fits = 0;
if (used + 1 < capacity) text[used++] = (char)ch;
else fits = 0;
ch = getchar();
} while (ch != EOF && !isspace((unsigned char)ch));
text[used] = '\0';
return fits;
}
int readInt(int *value) {
char token[64], *end;
if (!readToken(token, sizeof token)) return 0;
errno = 0; long result = strtol(token, &end, 10);
if (errno || end == token || *end || result < -1000000 || result > 1000000) return 0;
*value = (int)result; return 1;
}
int invalid(void) { puts("Invalid or incomplete input."); return 1; }
#include <math.h>
int hash(int key, int size) {
const double A = 0.6180339887498949;
double product = key * A, fraction = product - floor(product);
int index = (int)floor(size * fraction);
return index < size ? index : size - 1;
}
/* Nonnegative key (0..1,000,000), table size (1..1000). Index only, no table insertion. */
int main(void) {
int key, size;
if (!readInt(&key) || !readInt(&size) || key < 0 || size < 1 || size > 1000) return invalid();
printf("Hash index: %d\n",hash(key,size)); return 0;
}
Local compile: gcc -std=c17 multiplication-hash.c -o program -lm
Open in C compiler transfers the source. Copy the sample input into the compiler’s input box separately before running.
UNDERSTAND THE CODE
Line-by-line explanation
Follow the exact source above, from input handling through the algorithm and its result. Line numbers count blank lines; related statements are grouped into steps. Start with main to follow execution, then open the functions it calls.
Start with main → · Algorithm and data structure →
Lines 1–5Include input, conversion and validation declarations
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>stdio.h supplies terminal input/output. stdlib.h declares conversion and allocation functions. string.h supplies string and memory utilities. ctype.h supplies isspace, and errno.h supplies conversion error reporting. Headers declare facilities; including them does not execute an operation.
Lines 7–9Define a complete-token reader and initialise its state
/* Read one entire token; reject malformed, oversized and out-of-range input. */
int readToken(char text[], size_t capacity) {
int ch; size_t used = 0; int fits = 1;readToken receives a writable buffer and its capacity. ch is int so it can represent EOF as well as input characters. used counts stored characters, and fits starts as a success flag. One slot must remain for the null terminator.
Lines 10–11Skip leading whitespace and recognise end-of-file
while ((ch = getchar()) != EOF && isspace((unsigned char)ch)) { }
if (ch == EOF) return 0;Read until a non-whitespace character appears. The unsigned-char cast supplies an allowed value to isspace. Return 0 if EOF arrives before a token begins.
Lines 12–15Collect a token without silently truncating it
do {
if (ch == 0) fits = 0;
if (used + 1 < capacity) text[used++] = (char)ch;
else fits = 0;The do body handles the character already read. An embedded zero byte marks the token invalid. Store a character only while there is room for it and a null terminator; otherwise clear fits. Continue reading even after the buffer fills.
Lines 16–20Consume the full token and terminate the buffer
ch = getchar();
} while (ch != EOF && !isspace((unsigned char)ch));
text[used] = '\0';
return fits;
}Read through the next whitespace or EOF, add a null character after the stored prefix, then return fits. A too-long token returns 0 rather than pretending that its stored prefix is the complete token.
Lines 21–23Wrap token input as a checked integer read
int readInt(int *value) {
char token[64], *end;
if (!readToken(token, sizeof token)) return 0;value is an output address. A 64-slot local buffer receives the token; a failed token read returns 0 before attempting conversion.
Lines 24–25Convert base-ten text and check the whole result
errno = 0; long result = strtol(token, &end, 10);
if (errno || end == token || *end || result < -1000000 || result > 1000000) return 0;Reset errno before strtol. &end receives the stopping address. Reject conversion errors, no converted digits, leftover characters, or values outside -1,000,000 to 1,000,000. Thus 12x is not accepted as the integer 12.
Lines 26–28Return the accepted value or a standard error status
*value = (int)result; return 1;
}
int invalid(void) { puts("Invalid or incomplete input."); return 1; }Store a validated integer through *value and return 1 from readInt. invalid is a separate helper that prints the error and returns 1 as main’s failure status; the same number has different roles in these two function contracts.
Lines 30–32Include floor and choose the multiplier
#include <math.h>
int hash(int key, int size) {
const double A = 0.6180339887498949;math.h declares floor; the GCC compile command links the math library. A is a fixed fractional constant between zero and one, used by this multiplication-method example.
Lines 33–34Keep the fractional product and scale it to the table
double product = key * A, fraction = product - floor(product);
int index = (int)floor(size * fraction);Compute key*A, subtract floor(product) to keep its fractional part, multiply that fraction by size, then take floor for an integer index.
Lines 35–36Keep the returned index below the table size
return index < size ? index : size - 1;
}Normally the fractional method already yields a value below size. The conditional guards the upper boundary against floating-point rounding by returning size - 1 if necessary.
Lines 37–38Document an index-only hash demonstration
/* Nonnegative key (0..1,000,000), table size (1..1000). Index only, no table insertion. */
int main(void) {The input is a nonnegative key followed by a table size. The program calculates an index only; it does not store keys or resolve collisions.
Lines 39–40Read a valid key and positive table size
int key, size;
if (!readInt(&key) || !readInt(&size) || key < 0 || size < 1 || size > 1000) return invalid();readInt already limits integer magnitude. Require key >= 0 and size from 1 to 1000, preventing zero-divisor calculations and negative table indexes.
Lines 41–42Print the calculated index and finish
printf("Hash index: %d\n",hash(key,size)); return 0;
}Call hash(key,size), display the returned index and return success. A valid hash index need not be unique: different keys can still collide.
FOLLOW THE VALUES
Dry run
- For key 10, kA is approximately 6.1803398875.
- Its fractional part is approximately 0.1803398875.
- With m = 100, scaling gives approximately 18.03398875.
- Floor returns index 18.
CHECK THE BEHAVIOUR
Sample input & output
These outputs were captured by running the exact C source shown above. Commands use the stated input format; the programs omit repeated menu prompts so the same sample can be pasted and run.
Sample 1
10 100
Hash index: 18
Sample 2
1234 100
Hash index: 65
Sample 3
0 11
Hash index: 0
Common mistakes
- Do not use the whole product in place of its fractional part.
- State the constant instead of writing sample output as varies.
- Floating-point boundary effects mean this implementation is not exact real arithmetic.
WHY THIS GROWTH RATE?
Time and space complexity
O(1) time and O(1) extra space for the bounded double computation.
The algorithm performs a fixed number of arithmetic and floor operations independent of the number of stored keys.
The constant-time model assumes the bounded native numeric types used here. Input/output conversion and arbitrary-precision variants have separate costs.
The result chooses an initial index; it offers no guarantee against collisions.
Big-O describes growth in the stated input quantity. The educational limits keep examples safe; complexity discussions state which bounds depend on a generalized input size. Arithmetic on the accepted native numeric types is treated as constant-time.
PREPARE FOR YOUR LAB VIVA
Related viva questions & answers
Try answering aloud, then expand the answer to check your reasoning. These questions focus on the concepts used in this program.
Why is only the fractional part of k times A used?
It lies in the interval [0,1), which can be scaled to the table range. The whole product would generally produce an out-of-range index.
Why is floor used after scaling the fraction by m?
It converts the scaled value to the lower integer slot, following the hash formula. Rounding to the nearest integer could yield m near the upper boundary.
Why is A required to be between zero and one?
The standard multiplication method uses such a fractional multiplier to spread key-dependent fractional products. This example fixes A explicitly for consistent behavior.
Why was float replaced with double in the manual adaptation?
Float has fewer significant digits and can lose more fractional information as key magnitude grows. Double reduces that loss within this bounded teaching range.
Why should the example publish an actual numeric output?
The constant, key and table size determine a concrete result for this implementation. Saying varies hides the calculation students should be able to reproduce.
Does multiplication hashing require the table size to be prime?
The formula does not require a prime m. Distribution still depends on the constant and key data, so freedom of table size is not a promise of collision-free results.