how do i make my c program randomly select - c

I want to make a C program which randomly selects 6 numbers from numbers the range 1 - 37, without repeating any previously permutations. For example, suppose the program randomly selects 1,2,3,4,5,6. If the next permutation is randomly selected as 2,1,3,4,5,6, then that is OK. However, if 1,2,3,4,5,6 is selected again, that this is not OK. I want this to continue until there are no more possible sets available. How would I go about writing this C program?

Use the Knuth Shuffle. Gives you O(n) asymptotic complexity.
#include <stdlib.h>
#include <string.h>
int rrand(int m)
{
return (int)((double)m * ( rand() / (RAND_MAX+1.0) ));
}
#define BYTE(X) ((unsigned char *)(X))
void shuffle(void *obj, size_t nmemb, size_t size)
{
void *temp = malloc(size);
size_t n = nmemb;
while ( n > 1 ) {
size_t k = rrand(n--);
memcpy(temp, BYTE(obj) + n*size, size);
memcpy(BYTE(obj) + n*size, BYTE(obj) + k*size, size);
memcpy(BYTE(obj) + k*size, temp, size);
}
free(temp);
}
Ref: http://rosettacode.org/wiki/Knuth_shuffle#C

Now the KNUTH shuffle answer posted below is quite elegant. But it doesn't meet a particular requirement set forth by the OP in his question.
The OP said he wanted to be able to select all sets in random order until his program has consumed them all. So here goes. For purposes of demonstrate, we'll understand "select" to mean "print".
The total number of possible sets of "6 unique digits in the range of 1-37" can be expressed as:
TOTAL_NUMBER_OF_SETS = 37*36*35*34*33*32 = 1673844480
1673844480 (1.6 billion) fits nicely within a signed 32-bit number. And every unique set could potentially be assigned a unique integer id.
So... if you can generate a random number between [0,1673844479], we can map that to a very specific set of 6 unique integers.
To construct the set, we'll need a helper function that will allow us to keep track of which values between 1-37 have already been used during the iteration process of constructing the set. Then a little modulo arithmetic math to help us map an ID number to it's set of 6-digits:
#include <stdio.h>
#include <stdlib.h
#include <stdint.h>
const uint32_t TOTAL_NUMBER_OF_SETS = 37*36*35*34*33*32; // = 1673844480
// returns the Nth value value from the ordered set {1,range},
// skipping over elements previous selected
int GetAvailableElementFromSet(int n, int range, int inuse[])
{
int i = 0, x;
for (x = 0; x < range; x++)
{
if (inuse[x] == 0)
{
if (i == n)
{
inuse[x] = 1;
return x + 1; // +1 since the loop variable has a zero-based index
}
i++;
}
}
return -1; // error
}
void GetSpecificSet(uint32_t setindex, int output[])
{
int index;
int inuse[37] = {}; // boolean array of elements already picked for the output set. zero-init to all false
int j,k;
if (setindex >= TOTAL_NUMBER_OF_SETS)
return; // error!
for (j = 0; j < 6; j++)
{
index = setindex % (37-j);
output[j] = GetAvailableElementFromSet(index, 37, inuse);
setindex = setindex / (37-j) ;
}
}
And just to prove that this works, we can have another function iterate over all sets:
void PrintSet(uint32_t setindex)
{
int output[6];
GetSpecificSet(setindex, output);
printf("%d, %d, %d, %d, %d, %d\n", output[0], output[1], output[2], output[3], output[4], output[5]);
}
void PrintAllSetsInOrder()
{
uint32_t index;
for (index = 0; index < TOTAL_NUMBER_OF_SETS; index++)
{
PrintSet(index);
}
}
Now the program above will print out all the sets starting from:
{1,2,3,4,5,6} // first set
{2,1,3,4,5,6} // second set
{3,1,2,4,5,6} // third set
And ending with
{36, 37, 35, 34, 33, 32} // next to last set
{37, 36, 35, 34, 33, 32} // last set
And then obviously to print a random set:
void PrintRandomSet()
{
PrintSet(rand() % TOTAL_NUMBER_OF_SETS);
}
But the OP wanted all sets printed in random order without repeats. This gets tricky, because we have to keep track of random number values previously generated. I can think of several ways to do this. The most obivous candidate solution is to keep a bitmask comprised of TOTAL_NUMBER_OF_SETS bits. That is:
#define IS_BIT_SET(bmask, bitindex) (bmask[bitindex/8] & (0x01<<(bitindex%8)))
#define SET_BIT(bmask, bitindex) {bmask[bitindex/8] |= (0x01<<(bitindex%8));}
uint8_t* bitmask = calloc(TOTAL_NUMBER_OF_SETS/8 + 1);
That's about 200MB of memory allocated. Large, but workable. Then we keep picking random numbers from the range [0-TOTAL_NUMBER_OF_SETS), checking the bitmask if it's already been used, then call PrintSet with the random number after setting its bitmask position. Repeat until all TOTAL_NUMBER_OF_SETS have been printed.
Pseudo code for a working, yet problematic solution
for (x = 0; x < TOTAL_NUMBER_OF_SETS; x++)
{
index = rand()%TOTAL_NUMBER_OF_SETS;
while (IS_BIT_SET(bitmask, index))
{
index = (index + 1) % TOTAL_NUMBER_OF_SETS;
}
SET_BIT(bitmask, index);
PrintSet(index);
}
Now this should work just fine. But it's going to get dog-slow as the bitmask array starts to get filled up. The later iterations will spend most of its time just scanning the array of bits looking for an unset index value. There have been other discussions on StackOverflow on how to do efficient and uniform permutations of large sets. Perhaps a database is warranted. Go search for those solutions and apply it here for the win.

Related

To speed up C-code random selection with further modification array

I'm trying to write some code in C-language. The main idea is that I have an input linear array that consists the readius for each pixel (`````` - something like that, moreover, the length of pix_r, for instance, for picture with size (128,512) will be 128 * 512). And I need for each radius random selected fixed numbers of pixels and other set to -1. What I mean:
r = 2 in pix_r = [1, 8, 2, 2, 4, 6, 7, 7, 8, 2, 8] is in the following positions currentR = [2, 3, 9], and let's NumberOfRandomS = 2, so one of the possible result can be pix_r = [*, *, 2, -1, *, *, *, *, *, 2, *]. and the same should be doe for each r. If number of items == r is less than NumberOfRandomS, we should pick up all elements without any modification.
I try to write this in C-code. But I am a newbie and don't know all features and tips for optimization. My first aprroach of writing this function is
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <math.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdint.h>
#include <stddef.h>
#include <limits.h>
#include <string.h>
#include <ctype.h>
const int NumberOfRandomS = 5;
void RandomSelected(size_t numEl, int maxRad, int *pix_r){
srand(time(NULL));
int lenRandomIndex = NumberOfRandomS*sizeof(int);
int* RandomIndex = (int*) malloc(lenRandomIndex);
memset(RandomIndex, 0, lenRandomIndex);
int lenNumPerShell1 = (maxRad) * sizeof(int);
int* numPerShell1 = (int*) malloc(lenNumPerShell1);
memset(numPerShell1, 0, lenNumPerShell1);
//Calculate the number of each pix_r per shell
for (int i=0; i<numEl; ++i){
numPerShell1[pix_r[i]]++;
}
//Main part for random selection of NumberOfRandomS items
//for each pix_r
for(int r=0; r<maxRad; ++r){
int lenShellR = numPerShell1[r];
//if number of items for this r is less than should be
//selected, skip it. It means that we selected all items
//for this r
if(lenShellR <= NumberOfRandomS){
continue;
}
int lenCurrentR = lenShellR*sizeof(int);
int* currentR = (int *) malloc(lenCurrentR); // array of indexes for this r
memset(currentR, 0, lenCurrentR);
//filling currentR array with all indexes for this r
int cInd = 0;
for(register int j=0; j<numEl; ++j){
if(pix_r[j] == r){
currentR[cInd] = j;
cInd++;
}
}
//generate random indexes without repetiotion that should be selected from currentR
//this indexes help us to save r value in these positions and others indexes for this r
//set to -1
int value[NumberOfRandomS];
for (int i=0;i<NumberOfRandomS;++i)
{
int check; //variable to check or index is already used for this r
size_t pick_index; //variable to store the random index in
do
{
pick_index = rand() % lenShellR;
//check or index is already used for this r:
check=1;
for (int j=0;j<i;++j)
if (pick_index == value[j]) //if index is already used
{
check=0; //set check to false
break; //no need to check the other elements of value[]
}
} while (check == 0); //loop until new, unique index is found
value[i]=pick_index; //store the generated index in the array
RandomIndex[i] = currentR[pick_index];
}
//set all positions for each r that are not on random selected to -1
for(register int k=0; k < lenShellR; ++k)
{
int flag = 0; // flag will be 1 if this index for this r in RandomIndex
for (register int q = 0; q < NumberOfRandomS; ++q)
{
if(RandomIndex[q] == currentR[k])
{
flag = 1; //this index is found
}
}
if(flag != 1)
{
//index for this r not in RandomIndex, so set this index for this r to -1
pix_r[currentR[k]] = -1;
}
}
}
return;
}
I tried to optimize a little bit, but different resources contradict each other and after testing it didn't show any speeding up:
void ModRandomSelected(size_t numEl, int maxRad, int *pix_r){
srand(time(NULL));
int lenRandomIndex = NumberOfRandomS*sizeof(int);
int* RandomIndex = (int*) malloc(lenRandomIndex);
memset(RandomIndex, 0, lenRandomIndex);
int lenNumPerShell1 = (maxRad) * sizeof(int);
int* numPerShell1 = (int*) malloc(lenNumPerShell1);
memset(numPerShell1, 0, lenNumPerShell1);
//Calculate the number of each pix_r per shell
for (int i=numEl-1; i>=0; --i){
numPerShell1[pix_r[i]]++;
}
//Main part for random selection of NumberOfRandomS items
//for each pix_r
for(int r=maxRad-1; r>=0; --r)
{
int lenShellR = numPerShell1[r];
//if number of items for this r is less than should be
//selected, skip it. It means that we selected all items
//for this r
if(lenShellR <= NumberOfRandomS){
continue;
}
int lenCurrentR = lenShellR*sizeof(int);
int* currentR = (int *) malloc(lenCurrentR); // array of indexes for this r
memset(currentR, 0, lenCurrentR);
//filling currentR array with all indexes for this r
int cInd = 0;
for(register int i = numEl-1; i>=0; --i)
{
if(pix_r[i] == r){
currentR[cInd] = i;
cInd++;
}
}
//generate random indexes without repetiotion that should be selected from currentR
//this indexes help us to save r value in these positions and others indexes for this r
//set to -1
int value[NumberOfRandomS];
for (int i=NumberOfRandomS-1; i>=0; --i)
{
int check; //variable to check or index is already used for this r
size_t pick_index; //variable to store the random index in
do
{
pick_index = rand() % lenShellR;
//check or index is already used for this r:
check=1;
for (int j=0;j<i;++j)
if (pick_index == value[j]) //if index is already used
{
check=0; //set check to false
break; //no need to check the other elements of value[]
}
} while (check == 0); //loop until new, unique index is found
value[i]=pick_index; //store the generated index in the array
RandomIndex[i] = currentR[pick_index];
}
//set all positions for each r that are not on random selected to -1
for(register int k=lenShellR-1; k >= 0; --k)
{
int flag = 0; // flag will be 1 if this index for this r in RandomIndex
for (register int q = NumberOfRandomS-1; q >= 0; --q)
{
if(RandomIndex[q]== currentR[k]){
flag = 1; //this index is found
}
}
if(flag != 1)
{
//index for this r not in RandomIndex, so set this index for this r to -1
pix_r[currentR[k]] = -1;
}
}
}
return;
}
I will be very thankful if you help and explain what and how I can improve this function.
The code is rather messy and hard to follow, so I can't be bothered to figure out what it actually does. The algorithm overall might be the true bottleneck. Anyway, here's some misc comments & advise of potential problems that I spotted:
Ensure to only call srand once in the whole program.
The register keyword is obsolete, from a time when compilers were bad at determining when to place variables in registers. Nowadays, compilers are more competent at this than programmers, don't use register, it is bloat.
Similarly, replacing up-counting loops with down-counting ones for the sake of performance is an obsolete technique nowadays sorting under "pre-mature optimization". The compiler can do that optimization for you - so write the code as readable as possible instead.
Avoid iterating over the same range/array multiple times.
Keep loop conditions as trivial as possible. This helps readability and data cache optimization both. The ideal for loop should look like for(int i=0; i<n; i++).
malloc is much slower than static or local storage. In this case you have a few items and only need to access them locally, so all malloc calls should be swapped with local arrays. You may use VLA here, to get stack allocation instead. That is, drop this code:
int lenRandomIndex = NumberOfRandomS;
int* RandomIndex = (int*) malloc(lenRandomIndex);
memset(RandomIndex, 0, lenRandomIndex);
and replace with this code:
int RandomIndex [NumberOfRandomS];
You have similar situations all over the code. And you probably don't need to set it to zero, because:
Don't zero-initialize or memset arrays that you indeed to fill with data the first thing you do anyway. This is a rather big performance problem in the posted code.
Empty return ; at the end of a function returning void is just clutter.
Investigate if some of these searches could be replaced with binary search. It means sorting the data in advance but might lead to much faster code overall.
Minimize the amount of checks, particularly inside loops.
Split up your big monster functions into several. Local static functions are very certain to get inlined and they improve readability a lot. Splitting functions into several smaller also allows much easier benchmarking.
Please benchmark your code when optimizations are enabled.

Numeric palindromes from a given range and given length

So i wanna do this C problem where i need to read N (0<=N<=20) and M( 0<=M<=10) then print all numeric palindromes formed with numbers from {1....N} and of length M.
Input:
N=15
M=3
Output:
1 2 1
2 2 2
3 2 3
...
11 3 11
...
Things like 12 3 12 are not considered palindrome.
I tried to find compress this this palindrome to be just a number but it shows me numbers that are not supposed to be palindroms to.
Can you give me some hints on how to do this? Or if you can help me do this it would be very nice.
I'm not sure if this is what you were looking for but to be honest the problem intrigued me so I created a rather simplistic brute-force approach to it. Hope you'll find it useful.
The logic is the following:
Create a function that will iterate through all possible numbers(not digits) for a given position in our array of numbers.
Make that function call itself recursively until there is one instance of it handling each position in our array of numbers.
The instance of the function that is handling the last(rightmost) number will check each combination of numbers for being a palindrome. If one is it will be printed out.
Ah yes, to determine if a given array of numbers is a palindrome I used the simple approach to transform the array of numbers into an array of digits. So, in other words single digit numbers are transferred as is BUT double digit ones get separated into two digits and transferred as two entries. Once I trans-coded the array of numbers into an array of digits I just run a simple algorithm that starts from both ends of the digits array moving towards the middle checking each pair for differences. If no difference is found the array of digits (and consequently the array of numbers it comes from) is a palindrome.
That is basically it.
I'll supply the code below. Hope I could help. If you need any clarification or have a comment (like if I totally missed the point of the question :)) just ask!
The code:
#include "stdint.h"
#include "stdbool.h"
#include "stdio.h"
#define NUMBER_ARRAY_SIZE (10UL)
#define DIGIT_ARRAY_SIZE (NUMBER_ARRAY_SIZE * 2UL)
bool isPalindrome(uint32_t numbers[], uint32_t lastIndex)
{
bool retVal = true;
uint32_t digits[DIGIT_ARRAY_SIZE];
int32_t digitsLastIndex = -1;
// Turn number-array into a digit-array.
for(uint32_t pos = 0u; pos <= lastIndex; pos++)
{
if(numbers[pos] < 10u)
{
// single digit => transfer it 1on1
digits[++digitsLastIndex] = numbers[pos];
}
else
{
// double digits => split in two digits
uint32_t firstDigit = numbers[pos]/10u;
uint32_t secondDigit = numbers[pos] - (firstDigit * 10u);
digits[++digitsLastIndex] = firstDigit;
digits[++digitsLastIndex] = secondDigit;
}
}
// This is where we check if we really have a palindrome formed by all the digits.
uint32_t numOfSteps = (digitsLastIndex + 1u) / 2u;
for(uint32_t i = 0u; i < numOfSteps; i++)
{
if(digits[i] != digits[digitsLastIndex - i])
{
retVal = false;
break;
}
}
return retVal;
}
void processNumberAtGivenPosition(uint32_t positionOfNumber, uint32_t lastAllowedPosition, uint32_t largestNumberAllowed, uint32_t numbers[])
{
// Generate and process all numbers in the actual position.
for (uint32_t number = 1u; number <= largestNumberAllowed; number++)
{
// Update the number-array with the actual number(at the actual position).
numbers[positionOfNumber] = number;
if(positionOfNumber == lastAllowedPosition)
{
// We are at the last position already. Check if the current number-array is a palindrome..
if(isPalindrome(numbers, lastAllowedPosition))
{
// ..if yes, then print it on the screen.
for (uint32_t i = 0u; i <= lastAllowedPosition; i++)
{
printf("%u", numbers[i]);
}
printf("\n");
}
}
else
{
// We still have more positions on the right. Move on to the next(->) one.
processNumberAtGivenPosition(positionOfNumber + 1u, lastAllowedPosition, largestNumberAllowed, numbers);
}
}
}
int main()
{
uint32_t N, M;
scanf("%u", &N);
scanf("%u", &M);
uint32_t numberArray[NUMBER_ARRAY_SIZE];
processNumberAtGivenPosition(0u, M - 1u, N, numberArray);
}

How can I find the largest number in an array that is less than or equal to a random integer?

I am working on an assignment and I'm asked to create an array of fibonacci numbers in a range of 0 to 50,000. Once this array has been initialized I am suppose to create a random number between 2 and 10,000. Then, I'm suppose to compare the members of the fibonacci array with the random number to find the greatest fibonacci number that is less than or equal to the random number.
This is the code that I have so far, it correctly creates the array of fibonacci numbers and the random number. How would I start with comparing the members of the array to the random number?
#include <stdio.h>
#include <string.h>
#include <time.h>
void Get_Fibonacci(int n)
{
int fibArray[25];
int lower = 2, upper = 10000, count = 1;
int i, FibRange = 50000;
int first = 0, second = 1, next = 1;
printf("%d %d", first, second);
//Create fibonacci sequence between 0 and 50,000 and store in array
for (i = 2; (first + second) < FibRange; i++)
{
next = first + second;
fibArray[i] = next;
printf(" %d\n", fibArray[i]);
first = second;
second = next;
}
//Create Random Number between 2 and 10,000
srand(time(0));
int k;
for (k = 0; k < count; k++)
{
n = (rand() % upper - lower + 1) + lower;
}
}
I did a little tweaking to your algorithm. This should do what you are asking.
Basically since the Fibonacci sequence combines of sorted numbers, you can do binary search. Also, in your implementation, your array doesn't have to be of size 25 since you are only holding 23 integers. 0 and 1 are saved in independent variables. In addition, your random number generator was wrong.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX_N 10000
#define MIN_N 2
void Get_Fibonacci()
{
int fibArray[25];
int lower = 2, upper = 10000, count = 1, middle = 0,found=0;
int low=0,high=0;
int i, FibRange = 50000,n;
int first = 0, second = 1;
printf("\n\t Fibonacci sequence:\n");
fibArray[0]=0;
fibArray[1]=1;
printf("%d\n%d\n",fibArray[0],fibArray[1]);
/* Creates a fibonacci sequence between 0 and 50,000 and store in an array */
for (i=2; (first+second)<FibRange; i++)
{
fibArray[i]=first+second;
first=second;
second=fibArray[i];
printf("%d\n",fibArray[i]);
}
high=i-1 /* Using the for loop exit condition, as chux suggested */
/* Generates a random number between 2 and 10,000 */
srand(time(0));
n = rand()%(MAX_N+1-MIN_N)+MIN_N;
/* Binary search algorithm */
while (low<=high&&!found)
{
middle=(low+high)/2;
if (n==fibArray[middle])
{
count=fibArray[middle];
found=1; /* To terminate the loop if we have an exact match */
}
else if (n<fibArray[middle])
{
high=middle-1;
}
else
{
low=middle+1;
count=fibArray[middle]; /* Saving the number less than key value */
}
}
printf("\n\tRandom number was: %d\n",n);
printf("\n\tClosest match was: %d\n",count);
return;
}
int main(void)
{
Get_Fibonacci();
return 0;
}
First, need to claify somethings:
1) the for loop for creating a random number is useless since count is always is one
2) n should not be a parameter for the function since you generate a random number in the function
3) the i should start from 0, starting from 2 doesn't make any sense to me. You’re just wasting the first two elements in the array
Largest is the variable that carries the value of the largest element and still smaller than n.
int Largest = fibArray[0];
for(int counter=1; counter<25; counter++){
if(fibArray[counter]>Largest && fibArray[counter]<n)
Largest = fibArray[counter];
}
return Largest;
Lambda expression makes these sort of things significantly easier. I suggest learning about lambda and delegates to help with problems like this in the future

C How to Keep a Random Variable From Repeating the Same Number

So I'm just learning C and I would like to know how you could prevent a variable randomized with the rand() function from repeating the same number. I have a script which simply randomizes and prints a variable in a for loop 4 times. How could I make it so the variable never gets the same number after each time it uses the rand() function?
#include <stdio.h>
#include <stdlib.h>
int randomInt;
int main()
{
srand(time(0));
for (int i = 0; i < 4; ++i) {
randomInt = rand() % 4;
printf("%d\n", randomInt);
}
return 0;
}
On most machines, int is 32 bits. So after 232 iterations, you are sure that you'll get some repetition (and probably much before).
If you restrict yourself to much less loops, consider e.g. keeping an array of previously met random numbers (or some hash table, or some binary tree, or some other container).
For a loop repeated only 4 times, keeping an array of (at most 4-1) previously emitted numbers is quite simple, and efficient enough.
Read also about the pigeonhole principle.
A slightly different approach.
int set[] = {0, 1, 2, 3 } ;
srand(time(0));
shuffle(set,4);
using the shuffle algorithm given in this question
https://stackoverflow.com/a/6127606/9288531
I'm guessing that you are getting the same numbers because your are running your program multiple times within the same second. If time(0) hasn't changed, you will have the same seed and the same random numbers generated. Unless your program runs extremely quickly, I imagine using a seed based on microseconds instead of seconds would work:
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
int randomInt;
int main()
{
struct timeval my_microtimer;
gettimeofday(&t1, NULL);
srand(t1.tv_sec * my_microtimer.tv_usec);
for (int i = 0; i < 4; ++i) {
randomInt = rand() % 4;
printf("%d\n", randomInt);
}
return 0;
}
What you could do is keeping track of each number you already generated.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int hasMyNumberAlreadyBeenGenerated(int number, int generatedNumbers[], int size){
for(int i = 0; i < size + 1; i++){
//If you already generated the number, it should be present somewhere in your array
if(generatedNumbers[i] == number) return 1;
//If you did not, find the first available space in your array, and put the number you generated into that space
if(generatedNumbers[i] == 0){
generatedNumbers[i] = number;
break; //No need to continue to check the array
}
}
return 0;
}
int main()
{
int randomInt;
int generatedNumbers[4];
//We set "0" in all the array, to be sure that the array doesn't contain unknown datas when we create it
memset(generatedNumbers, 0x0, sizeof(generatedNumbers));
srand(time(0));
for (int i = 0; i < 4; ++i) {
randomInt = rand() % 4 + 1;
//As long as the number you generate has already been generated, generate a new one
while(hasMyNumberAlreadyBeenGenerated(randomInt, generatedNumbers, i) == 1){
randomInt = rand() % 4 + 1;
}
printf("generated : %d\n", randomInt);
}
return 0;
}
The problem with this method is that you can't generate a 0, because if you do you'll endlessly loop.
You can bypass this problem using a dynamic array using malloc() function.
If you want to write clean code you should define how many numbers you want to generate with a #define.
What you seem to be asking is a non-random set of numbers 0 to 3 in a random order. Given that;
int set[] = {0, 1, 2, 3 } ;
int remaining = sizeof(set) / sizeof(*set) ;
while( remaining != 0 )
{
int index = rand() % sizeof(set) / sizeof(*set) ;
if( set[index] > 0 )
{
printf( "%d\n", set[index] ) ;
set[index] = -1 ;
remaining-- ;
}
}
For very large sets, this approach may not be practical - the number of iterations necessary to exhaust the set is non-deterministic.

Refactoring Dynamic Approach for Optimal Binary Search Tree

I am very new to the concept of Dynamic Programing and CS in general. I am teaching myself by reading lectures posted online, watching videos and solving problems posted on websites such as GeeksforGeeks and Hacker Rank.
Problem
Given input
3 25 30 5
where 3 = #of keys
25 = frequency of key 1
30 = frequency of key 2
5 = frequency of key 3
I am to print the minimum cost if each key is arranged in a optimized manner. This is a optimal binary search tree problem and I found a solution on geeks for geeks that sort of does something similar.
#include <stdio.h>
#include <limits.h>
// A utility function to get sum of array elements freq[i] to freq[j]
int sum(int freq[], int i, int j);
/* A Dynamic Programming based function that calculates minimum cost of
a Binary Search Tree. */
int optimalSearchTree(int keys[], int freq[], int n)
{
/* Create an auxiliary 2D matrix to store results of subproblems */
int cost[n][n];
/* cost[i][j] = Optimal cost of binary search tree that can be
formed from keys[i] to keys[j].
cost[0][n-1] will store the resultant cost */
// For a single key, cost is equal to frequency of the key
for (int i = 0; i < n; i++)
cost[i][i] = freq[i];
// Now we need to consider chains of length 2, 3, ... .
// L is chain length.
for (int L=2; L<=n; L++)
{
// i is row number in cost[][]
for (int i=0; i<=n-L+1; i++)
{
// Get column number j from row number i and chain length L
int j = i+L-1;
cost[i][j] = INT_MAX;
// Try making all keys in interval keys[i..j] as root
for (int r=i; r<=j; r++)
{
// c = cost when keys[r] becomes root of this subtree
int c = ((r > i)? cost[i][r-1]:0) +
((r < j)? cost[r+1][j]:0) +
sum(freq, i, j);
if (c < cost[i][j])
cost[i][j] = c;
}
}
}
return cost[0][n-1];
}
// A utility function to get sum of array elements freq[i] to freq[j]
int sum(int freq[], int i, int j)
{
int s = 0;
for (int k = i; k <=j; k++)
s += freq[k];
return s;
}
// Driver program to test above functions
int main()
{
int keys[] = {0,1,2};
int freq[] = {34, 8, 50};
int n = sizeof(keys)/sizeof(keys[0]);
printf("Cost of Optimal BST is %d ", optimalSearchTree(keys, freq, n));
return 0;
}
However in this solution they are also taking input of the "keys", but it seems they have no impact on the final answer, as they shouldn't. Only the frequency of how many time each key is searched for matters.
For simplicity sake and understanding this dynamic approach, I was wondering how can I possibly modify this solution so that it takes its input in the format shown above and prints the result.
The function you presented does have a keys parameter, but it does not use it. You could remove it altogether.
Edit: in particular, since function optimalSearchTree() does not use its keys parameter at all, removing that argument requires changing only the function signature (...
int optimalSearchTree(int freq[], int n)
...) and the one call of that function. Since you don't need the keys for this particular exercise, though, you can altogether remove them from the main program, too, to give you:
int main()
{
int freq[] = {25, 30, 5};
int n = sizeof(freq)/sizeof(freq[0]);
printf("Cost of Optimal BST is %d ", optimalSearchTree(freq, n));
return 0;
}
(substituting the frequency values you specified for the ones in the original code)
The function does, however, assume that the frequencies are given in order of increasing key. It needs at least the relative key order to do its job, because otherwise you cannot construct a search tree. If you were uncomfortable with the idea that the key values are unknown, you could interpret the code to be using indices into the freq[] array as aliases for the key values. That works because a consequence of the assumption described above is that x -> keys[x] is a 1:1, order-preserving mapping from integers 0 ... n - 1 to whatever the actual keys are.
If the function could not assume the frequencies were initially given in increasing order by key, then it could first use the keys to sort the frequencies into that order, and then proceed as it does now.

Resources