Sorting a row of letters, using a blank space, with some added rules

  • Thread starter TheOriginalThemePark
  • Start date
T

TheOriginalThemePark

I will have a line consisting of the letters A-E in a random order and
a blank space at the end. So that gives me positions 1-6. Now I want
them sorted to give me the order "ABCDE ", and the sorting would be to
put a letter into the empty space, and sort them that way. That would
be easy enough to code if it wasn't for some necessary rules.

It will not be possible to swap from any position to any position,
which possible moves there are, has to be hardcoded in, like for
example a small array, but since there are maximum 15 possible moves,
that should not be a problem.

For example I could have this starting line-up "BCEAD ". And you could
only move between positions 1 and 2, 1 and 4, 1 and 6, 2 and 3, 2 and
5, 3 and 4, 3 and 6, 4 and 5, 5 and 6.

So since my goal is to minimize the number of moves, in this case the
algorithm would produce the following moves, which is the shortest
solution.

"BCEAD "
"BC ADE"
"B CADE"
" BCADE"
"ABC DE"
"ABCD E"
"ABCDE "

I hope I've made my point clear enough. What I can't figure out is the
algorithm for this, which I hope some of you can help me with. I don't
expect finished Java code, properly explained pseudo code will do :)

Michael
 
A

alexandre_paterson

I will have a line consisting of the letters A-E in a random order and
a blank space at the end. So that gives me positions 1-6. Now I want
them sorted to give me the order "ABCDE ", and the sorting would be to
put a letter into the empty space, and sort them that way. That would
be easy enough to code if it wasn't for some necessary rules.

It will not be possible to swap from any position to any position,
which possible moves there are, has to be hardcoded in, like for
example a small array, but since there are maximum 15 possible moves,
that should not be a problem.

For example I could have this starting line-up "BCEAD ". And you could
only move between positions 1 and 2, 1 and 4, 1 and 6, 2 and 3, 2 and
5, 3 and 4, 3 and 6, 4 and 5, 5 and 6.

So since my goal is to minimize the number of moves, in this case the
algorithm would produce the following moves, which is the shortest
solution.

"BCEAD "
"BC ADE"
"B CADE"
" BCADE"
"ABC DE"
"ABCD E"
"ABCDE "

I first misunderstood the problem: I didn't realize you had to
always swap with the space character.

Anyway...

Is it always 5 letters + 1 space character ?

Or could it be, say, 29 letters + 1 space character and as much
as 435 possible moves ?

Is there always a solution satisfying the given constraints?

For such a simple input, a very basic recursive method minimizing
cost works wonderfully:

- you take care not too recurse too deeply,
- you keep track of the path examined and the associated
cost (as to never visit a path while a cheaper path has already
been examinated),
- if the path hasn't been taken yet, compute the cost for every
possible move and keep the cheapest one.

Such a recursive method (that both compute the cost and keeps track
of the modification made) can be written in 15 lines of code.

If you're familiar with recursivity it's probably the easiest way to
solve such a problem (I doubt a non recursive solution can be
written as shortly and as elegantly for such a problem).

If the input grows bigger a "dumb" brute-force approach (recursive
or not) won't work. Adding memoization to your recursive method
could help drastically reduce the complexity of such a problem.

Then again, if you're input is only of six characters, I'd simply
write the simplest solution that works... For me it's recursivity.

Shortest path from "ECABD " to "ABCDE " given your
constraints ?

;)

Is it homework ? If not, what is it ?

See you later,

Alex
 
T

ThemePark

In order to make it as easy as possible, I've just decided that it's
always gonna be the letters A-E and one space. It's in order to solve
an online puzzle, I've found but also to try and learn some stuff about
that puzzle. But yeah, like you said, you always have to switch either
of the letter with the space character, just to underline that :)

Since it's from an online game, there's always a solution yes :) I'm
also quite familar with programming and I do know of recursive. I guess
what you're suggesting is to solve it like a small version of the TSP
problem.

I'm not sure what makes you say that it can be done in 15 lines of
code. But the way I see it, wouldn't there be a problem solving it that
way, as it could end up continously going from one position to another
position and back again, and therefore end up in a eternal loop?

Michael
 
A

alexandre_paterson

Hi again,


ThemePark wrote:
....
I'm not sure what makes you say that it can be done in 15 lines of
code.

hehe... The fact that I coded it in 15 lines of code after reading
your problem! (note that I said that the main recursive method
could be done in 15 lines of code, note the whole program).

:)

But the way I see it, wouldn't there be a problem solving it that
way, as it could end up continously going from one position to another
position and back again, and therefore end up in a eternal loop?

which is why you add, as I wrote, a check that verifies that
you're not recursing too deeply: if you're examinating a position
that you already examined (with a similar or highest cost), then
you stop recursing that path (for you know, for sure, that a
solution costing less has already been tried or is beeing tried).

Here's a small program you can cut'n'paste that finds the
minimal max_depth assumes that there will always be
a solution in less than 200 moves (which is very generous
given the problem's constraints).

(Note that I use "final's" everywhere for it helps my
wonderfull IDE to give me very interesting informations
due to its very elaborate code-analysis engine... But YMMV).

(Note also that I used an 120 columns wide editor to
code this small examples... YMMV and complaints to
/dev/null).

:)

This is working Java 1.5 code solving your problem:

--- BEGIN CUT HERE ---
import java.util.Map;
import java.util.HashMap;

public final class SwapLetters {

private static final int MAX = Integer.MAX_VALUE;
private static final int[][] moves = new int[][] {
{1, 2},{1, 4},{1, 6},{2, 3},{2, 5},{3, 4},{3, 6},{4, 5},{5, 6},
};

private final Map<String, Integer> m;
private int max_depth;

public static void main(final String[] args) {
final SwapLetters sl = new SwapLetters();
final String source = "BCEAD ";
final String target = "ABCDE ";
System.out.println("Minimal cost is: " + sl.min(target, source,
0));
}

public SwapLetters() {
m = new HashMap<String, Integer>();
max_depth = 200;
}

private int min(final String target, final String s, final int
cost) {
if (cost >= max_depth || (m.containsKey(s) && m.get(s) <= cost)
) {
return MAX;
}
if (s.equals(target)) {
max_depth = cost;
return cost;
}
m.put(s, cost);
int min = MAX;
for (final int[] iar : moves) {
if (s.charAt(iar[0] - 1) == ' ' || s.charAt(iar[1] - 1) ==
' ') {
final int swapcost = min(target, swap(s, iar[0] - 1,
iar[1] - 1), cost + 1);
min = swapcost < min ? swapcost : min;
}
}
return min;
}

private static String swap(final String s, final int l, final int
r) {
return "" + s.substring(0, l) + s.charAt(r) + s.substring(l +
1, r) + s.charAt(l) + s.substring(r + 1);
}

}
--- END CUT HERE ---


The recursive method contains 16 lines (18 with declarations) and that
can be reduced if needed (but it's not an obfuscation contest, is it?
:)

Every time a solution is found, the max_depth is lowered (we know
for sure it's unnecessary to try a path costing more than the solution
we found).

Note that here this only computes the minimal number of moves
needed, but that should give you a good starting point.

If you want to keep track of the moves, it's not much harder:
instead of associating a cost to the string you're processing
(Map<String, Integer>), you can associate the list of moves
made so far (eg Map<String, List<Integer>). The cost hence
simply becomes the size of the List<Integer> (and each integer
in the list represent the number of the move made [eg '2' for
"swap 1 and 6"]).

To see the algorithm at work, you may want to insert
System.out statements that adjusts the output given
the cost so far... (for testing, you'll also probably want
to lower the MAX_DEPTH value).

This could look, for example, like this:

cost 0 "BCEAD "
cost 1 " CEADB"
cost 2 "C EADB"
cost 3 " CEADB"
(dropping this path because: inferior cost exists)
cost 3 "CE ADB"
cost 4 "C EADB"
(dropping this path because: inferior cost exists)
..
..
..

As you can see it is not "continously going from one position
to another position and back again" for as soon as it
encounters a position (eg " CEADB") it checks the cost (3)
and notice that the same position with a lower cost (1) is
already being examinated.

If the method gives back Integer.MAX_VALUE then it means
either your problem has no solution or that the max_depth
given is too low (try changing the 200 to 4 to see what
happens).

As I said previously, this is a simple recursive brute-force approach
(with some precaution taken as to not recursive indefinitely or
unnecessarly) that works given your constraints.

Now wether this problem is NP-hard (like the TSP you cited) or
not is another matter :)

Hope it helps,

Alex (that likes to eat recursivity at breakfast ;)


P.S : If you like puzzle and optimization/minimization/maximization
problems, here's a famous one (many variations exists)... It is
interesting to solve both by hand and by writing a small
program (recursive of course).

5 pirates have a treasure of 100 gold pieces to split up
after a long and profitable trip. The pirates are ranked
by seniority and it is always the most senior pirate who
decides how to divide up the booty amongst himself and
any remaining pirates. However, the pirates are
democratic: if the most senior pirate does not get at
least 50% of the vote (including himself), then he is
killed and the process repeats itself with the nextmost
senior pirate proposing the breakup. The pirates are all
completely rational and know that their compatriots are
also completely rational. All of the pirates use the
following priorities the drive their voting: 1. They don't
want to get killed 2. They want to get the most money
possible 3. They want to kill other pirates. How does the
most senior pirate divide up the treasure such that he
keeps as much as he possibly can for himself?
 
C

Chris Uppal

It will not be possible to swap from any position to any position,
which possible moves there are, has to be hardcoded in

If you mean that the flow of control though your program has to be fixed in
advance (so that you can choose the smaller of two values, but you can't take
different paths based on which is smaller), then you should look into "sorting
networks" (Enter that term into your favourite search engine or online
encyclopaedia).

-- chris
 
A

alexandre_paterson

(e-mail address removed) wrote:
....
Here's a small program you can cut'n'paste that finds the
minimal max_depth assumes that there will always be
a solution in less than 200 moves

I meant to write:

Here's a small program you can cut'n'paste that finds the
mininal number of moves. Note that max_depth assumes
that there will always be a solution in less than 200 moves
 
T

ThemePark

Well, I've been looking at your program, and it works like a charm,
thank you. It does indeed find the minimum number of moves. I tried
adding the System.out.println, but quickly removed them again as it
turned out to write a LOT more information than I had expected and slow
the program down severely. But a nice addition none the less.

However I still have one problem. I would like to, as you said, be able
to get the list of the moves made, and I changed the lines of the
Map/Hashmap the way you suggested. However I got a lot of errors and
had to correct a lot of lines, and it got to a point where I had to
change so much, I couldn't figure out how to make it work with writing
out the list of moves. I was hoping you would be of assistance once
again, or somebody else for that matter, although it's quite late for
me to respond, I know :(

Michael
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,148
Messages
2,570,838
Members
47,385
Latest member
Joneswilliam01

Latest Threads

Top