How to simplify an extensive C if statement? - c

I am wondering what the best way is to approach this problem. I have a randomizing function set up that takes 8 strings as input and outputs a random one of them. I would like this randomizer to disregard all strings which have no value. For example, if I have strings text#, where # is 1-8, and let's say text5 and text7 have no text, then I want the randomizing function to check if any to use only use text#, where # is 1-8 but not 5 or 7.

Yikes! Put the strings in an array instead of having 8 different variables. Then use a for loop.

Count the list for valid strings.
Form random number (rand() % Count).
Find the matching string.
Sample code
int StringCount = 8;
char *String[StringCount];
// populate `Sting` somehow
// count valid strings
int Count = 0;
for (int i=0; i<StringCount; i++) {
if (ValidString(String[i])) Count++;
}
if (Count == 0) Handle_NoGoodStrings();
int random_number = rand()%Count;
int i;
for (i=0; i<StringCount; i++) {
if (ValidString(String[i])) {
if (Count == random_number) {
break;
}
}
}
// String[i] is the string

Put the strings in an array
Choose a random index into the array, using your random function
Use code similar to:
int i;
for (i = index; i != index; i = (i+1) % numitems)
{
/* check we have a string and it isn't empty */
if (strings[i] && *strings[i])
return strings[i];
}
return NULL;
assuming index is your random number between 0 and numitems -1.

Related

How to generate non-repeating numbers in C?

my first post here.
I'm trying to write a program in C, which generates a random password made of numbers, letters and capitals. The problem is that characters in password must NOT be repeated. I tried a few ways to prevent that, but nothing seemed to work.
void createPassword() {
char password[LENGTH];
char nums[] = "0123456789";
char letters[] = "abcdefghijklmnopqrstuvwxyz";
char caps[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int selector = rand() % 3; //random choice of character type
int i;
printf("Vytvorene heslo: ");
for(i = 0;i < LENGTH;i++) {
if(selector == 1) { //if selector == 1, add number to password etc.
password[i] = nums[rand() % 10];
printf("%c", password[i]);
selector = rand() % 3;
}
else if(selector == 2) {
password[i] = letters[rand() % 26];
printf("%c", password[i]);
selector = rand() % 3;
}
else {
password[i] = caps[rand() % 26];
printf("%c", password[i]);
selector = rand() % 3;
}
}}
I'll be glad if someone could tell me what to do next.
Picking a random index of an array is the same as picking the values of a shuffled array sequentially. I used Fisher–Yates shuffle Algorithm for shuffling of the array. After generating a shuffled array, just pick the index of the next character from the shuffled array, and use symbols[] to access the corresponding character from it. Also. I used srand(time(0)) to give a random seed for the random number generator. Include time.h for using time(0).
void createPassword() {
char password[LENGTH];
int total = 10+26+26;
char symbols[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
int i;
int ar[total];
for(i = 0; i < total; i++){
ar[i] = i;
}
srand(time(0));
for (i = total-1; i >= 1; i--){
// get random 0 <= temp <= i
int temp = rand() % (i+1);
// Swap ar[temp] and ar[i]
int temp2 = ar[i];
ar[i] = ar[temp];
ar[temp] = temp2;
}
printf("Vytvorene heslo: ");
for(i = 0;i < LENGTH;i++) {
password[i] = symbols[ar[i]];
printf("%c", password[i]);
}
}
That's not a big deal, random seeds are similar for several runs in C.
You may need to set the random seed to time(0) by adding something like this, first of your code:
srand(time(0));
You should import time.h too.
#include <time.h>
Besides if you want to make a password consisting numbers and alphabets at the same time you may need to move selector assignment into the loop.
An easy way to force random numbers with no repeating letters can be implemented by using an array of elements, that will play the role of a card deck.
Each time you get a card, you get it from the rest of the deck (there's a point that differentiates cards that have been already xtracted with cards that are still to be output) You select a random number between 0 to n-1 where n-1 is the number of cards left in the deck. Once extracted, you switch the card extracted with the first of the group that is still to be extracted, and advance the point one position behind it, so it becomes already extracted and is not selected again.
A sample implementation is shown below, in the function extract, which uses an array of cells to store the available objects to print (they can be anything, they are char in the given implementation to be able to produce what you want ---random strings with non repeating characters):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
typedef char cell;
/* cell is a type (the above definition allows to be generic)
* array is the array of cards still to be output, n is its size.
* which is the card we select from the array, so it must be in
* range 0..n-1. */
cell extract(cell *array, size_t n, int which)
{
if (which) {
/* exchange position n with position 0 */
cell temp = array[0];
array[0] = array[which];
array[which] = temp;
}
return array[0];
}
/* this is a simple main program to illustrate how to use the
* function above. */
int main(int argc, char **argv)
{
unsigned N = 10;
unsigned seed = 0;
/* we use an array, because we need to modify it. */
char alpha[1024] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int opt;
int show_seed = 0;
while ((opt = getopt(argc, argv, "n:s:a:S")) != EOF) {
switch (opt) {
case 'n': N = atoi(optarg); break;
case 's': seed = atoi(optarg); break;
case 'S': show_seed = 1; break;
case 'a': strncpy(alpha, optarg, sizeof alpha); break;
}
}
if (seed) srand(seed);
else {
sranddev();
srand(seed = (unsigned)rand());
if (show_seed) {
fprintf(stderr,
"seed = %u\n",
seed);
}
}
argc -= optind;
argv += optind;
int len;
cell *pos;
for (pos = alpha, len = strlen(alpha);
*pos && len && N--;
pos++, len--)
{
putchar(extract(pos, len, rand() % len));
}
puts(""); /* final \n */
}
The program's usage is:
deck [ -a string ][ -S ][ -s seed ][ -n N ]
where:
-a allows you to specify the string where characters will be taken from. Defaults to ABCDEF...XYZ
-S prints the seed used in the run, so you can specify it to initialize the random number generator.
-s specifies a seed from a previous run to generate the same sequence. Defaults to a random initialization based on sranddev() (FreeBSD)
-n specifies the number of characters to select from the string. Defaults to 10.
A sample run is:
$ deck -s 123456
UKWOACYZLI
$ deck -s 123456 -n 26
UKWOACYZLITPJHQESVGMRBXFDN
$ _
As you see no character is repeated.

Check if a string is included in an array and append if not (C)

I have 2 arrays, one called 'edges' which contains a list of city names and another called cityNames which is initialised as an empty string.
What I would like to do is move through the edges array element by element and see if it is included in the cityNames array. If it is, move onto the next element in edges, if it isn't, append the value to the cityNames array.
The code below adds the edges[i].startCity to the cityNames array but it does not check for duplicates and I can't figure out why.
for (int i = 1; i < noEdges; i++) {
for (int j = 0; j < noCities; j++) {
if(strcmp(edges[i].startCity, cityNames[j].cityName) != 0) {
strcpy(cityNames[i].cityName, edges[i].startCity);
}
}
noCities += 1;
}
Thanks in advance
I will assume that:
edges is an array of structures of a known length noEdges, each structure containing a string (either a char pointer or a char array)
cityNames is an array of structures for which the size is at least the number of distinct name (it could be noEdges or the size of the edges array)
the cityNames structure contain a char array element for which the size is at least the longest name + 1 (+1 for the terminating null)
Then the following code could give the unique names:
noCity = 0;
for (int i = 0; i < noEdges; i++) {
int dup = 0; // expect edges[i].startCity not to be a duplicate
for (int j = 0; j < noCities; j++) {
if(strcmp(edges[i].startCity, cityNames[j].cityName) == 0) {
dup = 1; // got a duplicate
break; // no need to go further ...
}
}
if (dup == 0) { // not a duplicate: add it to cityNames
strcpy(cityNames[noCities].cityName, edges[i].startCity);
noCities += 1; // we now have one more city
}
}
}
A good idea to start with would be to ditch working with strings if you can (or at least manipulate strings when actually needed).
You could start off by assigning each city name a number, that way you have an array of ints which is quicker and easier to work with.
Scanning for duplicates becomes trivial as you would now only be comparing numbers.
When you need to display the actual text on screen or write the city names to file, you could use the indexes associated with the city names to retrieve the appropriate textual representation of the index. You could then replace the data type of your cityNames[] to ints. This makes each 'node' which the 'edges' connect a number instead of text.
char* actualCityNames[n]; //array holding all city names with duplicates, could be a file also
char* indexedCityNames[n];//array with indexed cities (in order of appearance in actualCityNames, i.e. not alphabetical order)
//indexedCityNames will most likely not use up N slots if duplicates occur
//this is why there is a second counter for the size of indexed cities
int indexedCount = 0;//number of unique city names
int duplicates = 0;
//loop for actualCityNames slots
for(int i=0; i<n; i++){
//loop for indexedCityNames
for(int j=0; j<indexedCount; j++){
//strcmp returns 0 if both strings are the same
if(strcmp(actualCityNames[i],indexedCityNames[j]) == 0){
//duplicate found, mark flag
duplicates = 1;
}
}
if(!duplicates){
strcpy(indexedCityNames[indexedCount],actualCityNames[I]);
indexedCount++;
}
duplicates = 0;
}
Your code snippet does not check for duplicates because in the inner loop the if statement appends startCity as soon as a first cityName is encountered that is not equal to the current startCity.
Moreover in this statement
strcpy(cityNames[i].cityName, edges[i].startCity);
^^^
there is used an incorrect index.
And the variable noCities shall be incremented only when a new startCity is appended.
Also the outer loop should start from the index equal to 0.
Rewrite the loops the following way
int noCities = 0;
for ( int i = 0; i < noEdges; i++ ) {
int j = 0;
while ( j < noCities && strcmp(edges[i].startCity, cityNames[j].cityName) != 0 ) {
++j;
}
if ( j == noCities ) strcpy(cityNames[noCities++].cityName, edges[i].startCity);
}

How do I replace all occurrences in an array with another array in C

I want to replace all occurrences in an array (string) with another array.
I have a code that:
stores the string in an array in which the replacing is to take place output[],
another array that stores the string to be searched for as replace[] and a third array called toBeReplacedBy and the replacing of the first occurrence works just fine but it skips the other occurrences in the output
for example:
replace[]:
abc
toBeReplacedBy[]:
xyz
output[]:
abcdefabc
becomes
xyzdefabc
but it should become:
xyzdefxyz
I suspect the problem lies with the replacer code :
//the replacer
for (i = 0; i<80; i++) {
if (output[i] == replace[i])
output[i] = toBeReplacedBy[i];
}
//debug purpose
puts("output[]:\n");
puts(output);
return 0;
}
What have I done wrong here and how could I get it to replace all occurrences in the array.
please be aware that I only wish to use stdio.h to do this
thabks in advance
Never iterate further than the array length. This leads to undefined and possibly dangerous behaviour. If you only expect strings, use something like:
int i = 0;
while(output[i] != '\0')
{
// your logic here
i++;
}
Additionally you want to check for concurrent appearances of the same characters. But in your code you only check the first three characters. Everything after that is undefinded behaviour, because you cannot know what replace[3] returns.
Something similar to this could work:
int i = 0;
int j = 0;
int k;
while(output[i] != '\0')
{
if (output[i] == replace[j])
j++;
else
j = 0;
// replace 3 with the array length of the replace[] array
if (j == 3)
{
for(k = i; j >= 0; k-- )
{
output[k] = toBeReplacedBy[j]
j--
}
j = 0;
}
i++;
}
But please check the array boundaries.
edit: Additionally as Nellie states using a debugger would help you to understand what went wrong. Go through your program step by step and look how and when values change.
First advice is to try to debug your program if it does not work.
for (i = 0; i<80; i++) {
if (output[i] == replace[i])
output[i] = toBeReplacedBy[i];
}
There are two problems in this loop.
The first is that are iterating until i is 80. Let's look what happens when i becomes 3. output[3] in case of abcdefabc is d, but what is replace[3]? Your replacement array had only 3 letters, so you have to go back in the replacement array once you finish with one occurrence of it in the original string.
The second is that you check letter by letter.
Say you original array, which you named output somehow was abkdefabc, first three letters do not match your replacement string, but you will check the first two letters they will match with the replacement's first two letters and you will incorrectly change them.
So you need to first check that the whole replacement string is there and only then replace.
You should use strlen() to know length of your array or iterate until you reach the end of a your array ('\0').
'\0' and strlen are only available for array of char.
Your loop should looks like this :
int i = 0;
int len = strlen(my_string);
while (i < len)
{
//logic here
i = i + 1;
}
OR
int i = 0;
while (my_string[i] != '\0')
{
// logic here
i = i + 1;
}

Find if 2 strings are composed of same letters

I have a problem, this function should return 1 if secret is composed of same letters than letters_guessed.
It works fine, as long as letters_guessed has atleast 1 same letter which are in the secret. If there is same letter 2 times or more, it does not work. I know why, but I can not solve it because I can not remove same letters.
I can not remove same letters from letters_guessed array, because it is constant, and I can not change it to nonconstant.
Again ...
If:
secret = "cat"
letters_guessed = "txaoc"
return 1
**Right**
If:
secret = "dog"
letters_guessed = "gefxd"
return 0
**Right**
If:
secret = "car"
letters_guessed = "ccr"
return 1
**Wrong, How can I solve this?**
Sorry for my bad English and long explanation.
Here is my program:
int is_word_guessed(const char secret[], const char letters_guessed[])
{
int same = 0;
for(int i = 0; i < strlen(letters_guessed); i++)
{
for(int j = 0; j < strlen(secret); j++)
{
if(letters_guessed[i] == secret[j])
same++;
}
}
if (same == strlen(secret))
return 1;
else
return 0;
}
You can:
make a copy of your strings in order to flag already counted letters (since you tell you don't want to modify the strings, I suggest making a copy first in order to discard already counted letters);
get sorted versions of your strings and then compare them with a single loop; this solution would also provide a better complexity (you could get O(n log n) instead of your current O(n^2)).
One way to do this without modifying the strings is to count the occurrences of letters in the strings. When the guess has more occurrences of a letter than the secret, it's a miss. The case where a letter occurs in the guess that isn't in the secret is just a special case, because then the count of occurrences in the secret is zero.
In practice, you don't keep two separate counts: Add the letters of the guess to the count first, then remove the letters of the secret. As soon as one count drops below zero, it's a miss.
You can make use of the fact that there are only 256 different chars and keep the counts in an array. The index to the array is the letter's ASCII code. Be careful not to access the array at negative indices. C's char isn't guaranteed to be unsigned, so you could cast it or use an unsigned temporary variable or chose not to consider negative values.
Here's an implementation:
int contains(const char *guess, const char *secret)
{
int count[256] = {0}; // start with all-zero array
while (*guess) {
unsigned char c = *guess++;
count[c]++;
}
while (*secret) {
unsigned char c = *secret++;
if (count[c] == 0) return 0;
count[c]--;
}
return 1;
}
You can keep iteration in memory by maintaining an array of all 26 alphabets.
Assumptions:- All letters should be in lower case. Secret should not have repeated letters.
Logic:- Make array entry to 1 if we have considered that letter. 97 is ascii value of 'a'
// declare header file
#include "string.h"
int is_word_guessed(const char secret[], const char letters_guessed[])
{
int same = 0;
int alphabets[26];
// make all enteries 0
for (int k = 0; k <= 25; k++)
{
alphabets[k] = 0;
}
for (int i = 0; i < strlen(letters_guessed); i++)
{
for (int j = 0; j < strlen(secret); j++)
{
if (letters_guessed[i] == secret[j] && (alphabets[(char)letters_guessed[i] - 97] == 0))
{
same++;
alphabets[(char)letters_guessed[i] - 97] = 1;
}
}
}
if (same == strlen(secret))
return 1;
else
return 0;
}
It's easy.
In Haskell it would be:
all (`elem` letters_guessed) secret
in other words: All chars in secret must be in letters_guessed.
In C its (not tested):
// Iterate though string 'secret' until there is a char not
// part of 'letters_guessed'. If there is none, return 1
unsigned check(char *secret, char *letters_guessed) {
unsigned length_secret = length(secret);
unsigned length_guessed = length(letters_guessed);
for (int i = 0; i < length_secret; i++) {
if (!elem(secret[i], letters_guessed) {
return 0;
}
}
return 1;
}
// Check if char 'current' is part of 'string'
unsigned elem(char current, char *string) {
unsigned length = length(string);
unsigned found = 0;
for (int i = 0; i < length; i++) {
if (current == string[i]) {
return 1;
}
}
return 0;
}

Perform Selection Sort On 2D Char Array

I currently have a 2D char array size: [5][256].
The array can hold either numbers or letters.
I have been tasked with using the Selection Sort to sort the strings into ascending order.
My idea is to convert each row into ASCII and then sort the values in ascending order then convert back to chars.
Ive implemented a 2D Array Selection sort for another task, however, it doesnt work here as i coded it to work with 2 columns not 256 like here (not sure how to change it).
What i need help with is how do i use the ASCII value for each row and use it in a selection sort.
Been trying to figure this out for hours now, driving me mental.
Any help is appreciated.
Im not necessarily looking for someone to code everything for me, more of a kick in the right direction. Im new to C and not aware of every function C can do.
Here is my current code in full:
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
char arc5Strings[5][256];
int nCount, nCount2, nCount3, nCount4, nCount5, nCount6, nCount7;
int fMinVal[1][2] = {1,1};
int nMinValPosition;
int nMoves;
int nRow;
int fTemp[1][2] = {1,1};
int fTemp2[1][2] = {1,1};
//input the values
for(nCount=0; nCount < 5; nCount++)
{
printf("Please input string %d/5: ", nCount + 1);
fgets(arc5Strings[nCount], 256, stdin);
}
printf("\n\n");
//print entire array
for(nCount3 = 0; nCount3 < 5; nCount3++)
{
for(nCount4 = 0; arc5Strings[nCount3][nCount4] != '\0'; nCount4++)
{
printf("%d ", arc5Strings[nCount3][nCount4]);
//ASCII values outputted in a line instead of in array format when using %c
}
}
return 0;
}
Old 2D Array selection sort i devised - extracted from code:
//-----------------------------------
//set up the switch
for(nCount5 = 0; nCount5 < 5; nCount5++)
{
fMinVal[0][0] = arc5Strings[nCount5][0]; //min value is row 0 col 1
nMinValPosition = nCount5;
for(nCount6 = nCount5 + 1; nCount6 < 5; nCount6++)
{
if(arc5Strings[nCount6][1] < fMinVal[0][0])
{
fMinVal[0][0] = arc5Strings[nCount6][0];
nMinValPosition = nCount6;
}
/* Perform the switch - actually switch the values */
if(fMinVal[0][0] < arc5Strings[nCount5][0])
{
fTemp[0][1] = arc5Strings[nCount5][1];
fTemp2[0][0] = arc5Strings[nCount5][0];
arc5Strings[nCount5][1] = arc5Strings[nMinValPosition][1];
arc5Strings[nCount5][0] = arc5Strings[nMinValPosition][0];
arc5Strings[nMinValPosition][1] = fTemp[0][1];
arc5Strings[nMinValPosition][0] = fTemp2[0][0];
nMoves++;
}
}
}
//------------------------------
printf("\n\n");
printf("The sorted list, in ascending order, using selection sort, is:\n\n");
for(nCount3 = 0; nCount3 < 5; nCount3++)
{
for(nCount4 = 0; arc5Strings[nCount3][nCount4] != '\0'; nCount4++)
{
printf("%c", arc5Strings[nCount3][nCount4]);
}
}
printf("\n %d moves were made to sort this list\n", nMoves);
EDIT - RESULTS OF GEORGE'S ANSWER:
Input1 = 90
Input2 = 70
Input3 = abc
Input4 = 500
Input5 = 200
Sorted Array Results:
200
90
70
abc
500
You're on the right track. I would implement this as follows:
for(i=0;i<5;i++)
{
indexOfCurrentSmallest = i;
for(j=i;j<5;j++)
{
for(k=0;k<255;k++)
{
if(arc5Strings[j][k] < arc5Strings[indexOfCurrentSmallest][k])
{
//we found a new possible smallest
indexOfCurrentSmallest = j;
break;
}
else if(arc5Strings[j][k] > arc5Strings[indexOfCurrentSmallest][k])
{
//no point in searching further, the one we are looking at is already larger than the one we found.
break;
}
}
}
//here, we have found the actual smallest, let's do a swap
for(q=0;q<255;q++)
{
temp = arc5Strings[i][q];
arc5Strings[i][q] = arc5Strings[indexOfCurrentSmallest][q];
arc5Strings[indexOfCurrentSmallest][q] = temp;
}
}
I haven't tested this code, but it should be roughly what you're looking for. Basically, it compares ASCII values starting at the left, until it finds a difference, and stores the index for later swapping after comparing all 5 strings.
EDIT I've now tested the code above, and it works now.
First find each string length
int length[5];
for(i = 0, i < 5, i++){
length[i] = strlen(arc5Strings[i]);
}
Sort the lengths. Those with the same, compare the value of the first letter.
Thats it.
valter

Resources