I'm still investigating the problem with decimal to word conversion. I understood the basics, but now my goal is to learn how to add in base-26 (the English alphabet) or base-52 ([a-zA-Z]
) or base-62 ([a-zA-Z0-9]
), etc.
I've written the following code and it works, but I think it could be written better and run faster.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "setup.h"
/*[1]*/
struct Word {
uchar num[MAX_LEN];
char w[MAX_LEN+1], *alpha, *pass;
};
/*[2]*/
uchar conv(struct Word *wrd, uchar step, uchar p, uchar base) {
uchar k=wrd->num[p]+step;
if (k<=base) {
wrd->num[p]=k;
wrd->w[p]=wrd->alpha[wrd->num[p]-1];
return p;
} else {
uchar n=k/base, d=k-(base*n); // equal to k%base;
/*[3]*/
if (!d) {wrd->num[p]=base; --n;}
else wrd->num[p]=d;
/*[4]*/
wrd->w[p]=wrd->alpha[wrd->num[p]-1];
return conv(wrd,n,p-1,base);
}
}
// That seems to be OK, it was written only for testing.
int main(int argc, char **argv) {
if (argc <4 || argc >6) {
printf("\nUsage: %s start stop alpha [k] [d]\n", argv[0]);
puts(" \
\tstart - number to start from\n \
\tstop - number to stop at\n\talpha - alphabet to use\n \
\tk - (optional) print every k-th combination, every 1000th by default\n \
\td - (optional) generate combinations with step=d\n \
");
return -1;
}
uint PE=1000;
uchar step=1, maxlen=MAX_LEN, d=0, base=0;
struct Word p;
p.w[MAX_LEN+1]=0;
p.alpha=calloc(65,1);
ull stop=strtoull(argv[2],NULL,10), start=strtoull(argv[1],NULL,10),cnt=start;
strncpy(p.alpha, argv[3], MAX_ALPHA_LEN);
base=(uchar)strlen(p.alpha);
if (argc >= 5) PE=atoi(argv[4]);
if (argc == 6) step=(uchar)atoi(argv[5]);
/*[5]*/ memset(p.num,0,MAX_LEN);
/*[6]*/
for (;cnt;cnt=(cnt-1)/base,--maxlen) {
p.num[maxlen]=(cnt-1)%base+1;
p.w[maxlen]=p.alpha[p.num[maxlen]-1];
}
/*[7]*/
p.pass=&(p.w[++maxlen]);
for (;start<=stop;start+=step) {
if (!(start%PE)) puts(p.pass);
d=conv(&p, step, MAX_LEN, base);
/*[8]*/
if (d<maxlen) maxlen=d; p.pass=&(p.w[maxlen]);
}
free(p.alpha);
return 0;
}
/* setup.h */
typedef unsigned int uint;
typedef unsigned short ushort;
typedef unsigned long ull;
typedef unsigned char uchar;
#define MAX_LEN 64
#define MAX_ALPHA_LEN 127
Code explanation
That's a struct that contains
num[MAX_LEN]
, that holds numbers forw[MAX_LEN+1]
like this:num[]={1,2,3}
w[]={alphabet[0], alphabet[1], alphabet[2]}
That's a function responsible for adding decimal numbers to word. Returns current position in a word.
- "abcd" -> "abce" -> "abcf" -> ... -> "abc< last_letter_of_alphabet >"
That's what I don't like: these ifs/elses.
- Another thing I don't know how to get rid of: repetition of
wrd->w[p]=wrd->alpha[wrd->num[p]-1];
- All numbers in
num
should be zeroes. - That's called only once and converts the number we start from to word.
p.pass
is a pointer I'm using to print the wordsw[]=="\0\0\0ab";
->p.pass==&(w[3]);
That runs when a new character's added to word
You may find this part of code (uchar n=k/base, d=k-(base*n);
) a bit complicated, I'll explain it as well.
As the comment says, d=k-(base*n);
does the same as d=k%base;
. I'm using it because I was told that modulo operation on 64-bit integers will be (and it is) pretty slow on 32-bit platforms. Moreover, I already know the quotient, so I can easily calculate the remainder.
How to use
Compile that as an ordinary C program and run the following:
./gen <start from> <stop at> <alphabet> <print each N-th combination> <generate every N-th combination>
For example, ./gen 1 10 abcd 1 2
will result in the following
a
c
aa
ac
ba
It generates every 2nd combination and prints all generated words.
That program can add any number to the word. I've got another program that lacks the if/else statement ([3]) but fails to add a number if k%base
is zero (zeroes are not allowed).
What does this program do?
This program was made to generate permutations with repetition of a given alphabet. When you supply a number to start with, it converts it to a so called 'word' using the given alphabet. In other words, it computes the N-th permutation with repetition. The first is always the first letter of the given alphabet while the last is unknown as there is an infinite number of permutations. The permutations ('words') become longer and longer as the number (the position of each permutation) increases.
If you have an alphabet of length 62, you get 62 words of length 1, 62^2 words of length 2 and 62^k words of length k. The first permutation is alphabet[0]
, the 62-th is alphabet[62-1]
and the third is alphabet[0]+alphabet[0]
.
The goal is not to convert decimal numbers to any base but to generate permutations with repetition using bases.
Questions
- Is it possible to remove the if/else statement I was talking about in p. 3?
- Is it possible to get rid of code repetition I was talking about in p. 4?
- How can I speed up the code?