views:

75

answers:

2

I am looking to take a map<char, vector<char> > and generate each possible map<char, char> from it.

I understand this may use a sizeable amount of memory and take a bit of time.

Each map<char, char> needs to contain every letter a-z, and be mapped to a unique a-z character. ie. ak bj cp dy ev fh ga hb ir jq kn li mx nc oo pz qs rl sd te uw vf wg xm yu zt

Here is what I have concluded for myself so far:

To cut down the ridiculous number of possible combinations to a lower amount, if a vector<char> contains more than 5 elements, I will simply replace it with a vector<char> containing a single char from my 'master'/'original' map<char, char>.

Not all characters will be present over all of the vector<char>s in the map. These characters need to be found and put into some 'others' vector.

This should also contain characters where one character is the only possible character for more than one character key(ie. mw in the example I am working from - I'm unsure how to go about this).

This 'others' vector should be used for the cases where it is not possible to have a unique a-z character, or where more than one character has the same, single possible character.

Here’s an example of what I have so far.

I will be taking a map<char, vector<char> >, such as:

a: gjkpqvxz
b: gjkpqvxz
c: gjkpqvxyz
d: mw
e: gjkpqvxz
f: nr
g: at
h: cf
i: his
j: gjkpqvxz
k: r
l: h
m: gjkpqvxz
n: gjkpquvxyz
o: is
p: gjkpqvxz
q: is
r: dl
s: l
t: e
u: dgkpuvy
v: cf
w: bcf
x: dguy
y: f
z: at

This is my starting map. After cutting out the large character vectors of over 5 and replacing them with the best guess. Where the is a vector<char> of size 1, that character mapping only has one combination, and that character cannot be used in any other mapping as it would make it not unique. I have trimmed it down to:

a: k
b: j
c: p
d: mw
e: v
f: n
g: at
h: c
i: is
j: q
k: r
l: h
m: x
n: guy
o: is
p: z
q: is
r: d
s: l
t: e
u: dguy
v: c
w: bc
x: dguy
y: f
z: at

The 'others' vector contains 'o' (I think it is important to note that I think this should contain cases such as mw from the above example. As d is the only place mw can be used, but obviously with the need for each letter to only be used once, only one of them can be used, leaving the other to be lost somewhere. I'm not sure how to go about programming a general case to add these to the others vector.)

I am looking for help and pointers with generating every possible map<char, char> from map<char, vector<char> >s like this and in this format. They will be used as an argument in a function call. I'm not really sure where to start writing something that would work in a general sense. I would probably approach it with a large amount of for loops looking through every element against every other element against every other element ... etc etc, which I assume would be extremely inefficient and there are probably much more elegant ways of solving such a problem.

Sorry if this is too wall of text-ish or seems overly specific or poorly written/asked.

I appreciate any and all assistance.

A: 

I understand this may use a sizeable amount of memory and take a bit of time.

Yes, the number of combinations is about 403,291,461,000,000,000,000,000,000 :-)

FredOverflow
really that many? With such a reduced amount of characters and 16 of them only ever being 1 character, several of 2 and only a few above that?
Sam Phelps
As Steve Jessop points out below, it is closer to 6000 which is what I expected.
Sam Phelps
+1  A: 

I guess I'd hope that I don't need them all to exist simultaneously. Then I could:

1) Create the first map by assigning the first possible element to each letter:

for (char c = 'a'; c <= 'z'; ++c) {  // yes, I assume ASCII
   new_map[c] = old_map[c][0];
}
int indexes[26] = {0};

2) Create the remaining maps in turn by modifying the existing map, repeatedly:

++indexes[0];
if (indexes[0] < old_map['a'].size()) {
    new_map['a'] = old_map['a'][indexes[0]];
} else {
    indexes[0] = 0;
    new_map['a'] = old_map['a'][0];
    // "carry the 1" by applying the same increment process to indexes[1]
}
do_something_with(new_map);

do_something_with can re-construct the "others" vector each time from the map, or else you can update it each time you change a character. Replace:

    new_map['a'] = something;

with:

    char removed = new_map['a'];
    --counts[removed];
    if (counts[removed] == 0) others.add(removed);
    ++counts[something];
    if (counts[something] == 1) others.remove(something);
    new_map['a'] = something;

In your trimmed-down example there are only about 6000 possibilities, which should fly by. In fact, if you did need them all simultaneously you could copy the previous map at every step, and it wouldn't exactly take until the next ice age.

Btw, have you considered that a map is a bit overkill for only 26 possible keys, each of which is required to be present in every map? A vector or array would be considerably cheaper to use and to copy.

Steve Jessop
This looks like what I am looking for. Thanks. I'm having a little trouble understanding this, but I suspect that is because it is almost 1am. I'm guessing the 2nd part would need to be in some kind of loop? I will take a better look in the morning. Would it be ok to bother you if I can still not quite grasp what is going on?Thanks again.
Sam Phelps
Yes, the second part is in a loop. The loop should be broken if you try to "carry the 1" beyond indexes[25]. And actually you'd best define a function `increment_position(int)`, which recursively calls itself to carry the 1. It's just that I'm in GMT too, and wasn't confident I could type that all out without testing or falling asleep. There's at least one optimisation you could do: instead of starting at index 0 then carrying to 1,2 etc, make a list of all the indexes where the vector is bigger than size 1, and only bother incrementing at those indexes.
Steve Jessop
I am still having some trouble understanding the "carrying the 1" you refer to for the loop I should use and knowing when to brake out.
Sam Phelps
Think of it like classroom addition, except that instead of base 10, each "digit" (i.e. index in the array) is in a different base, given by the size of the corresponding vector. So if you try to increment indexes[0] but it has passed the limit, you set it to 0 and increment indexes[1] instead. If that passes the limit, you set it to 0 too and increment the next one. If you get all the way to the end setting things to 0 and run out of "digits", that means you're back to the map you originally started with (first option for every char), so you're finished.
Steve Jessop
One more qu before I figure this out or give up and settle for not trying all possible combos. This project is really taking up a disproportionate amount of my time. I'm guessing there is some major logic flaw in my implementation of your loop, as the program exits when run. `while(true){ ++indexes[i]; for (char ch = 'a'; ch <= 'z'; ++ch) { if (indexes[i] < a[ch].size()) { compmapguess[ch] = a[ch][indexes[i]]; } else { indexes[i] = 0; // cout<< "Shouldnt get here that often" << endl; compmapguess[ch] = a[ch][i]; i++;} if (i>25){ break;}` Urgh, sorry about formatting.
Sam Phelps
To check your code, it should first generate abcdefghijklmnopqrstuvwxyz -> kjpmvna... Next is kjpwvna... then kjpmvnt. Any other first 3 guesses is wrong.
Steve Jessop