Open a lock with the least number of moves
Asked Answered
P

3

5

Consider a lock, made of a system of wheels. Each wheel has 26 letters of the alphabet, in order, and each wheel is initialized with 'a'. If you move one wheel up, the display for that wheel moves to the next letter of the alphabet; moving a wheel down, on the other hand, switches the display to the previous letter of the alphabet. For example:

['a'] ->  UP  -> ['b']
['b'] -> DOWN -> ['a']
...
['z'] ->  UP  -> ['a']
['a'] -> DOWN -> ['z']

One can move any contiguous subsequence of wheels in the same direction with just a flick. This has the same effect of moving all the wheels of the subsequence that way, with a single motion. For example, if the target string is 'zzzzzzzzz', a single movement, changing 'a' to 'z', will change the entire sequence of wheels from 'a' to 'z', thus reaching the target string -- opening the lock.

How can I determine the least number of moves to open a lock? Is there a dynamic solution for this problem? The algorithm must yield the following results:

           Target string         | # moves
   ______________________________ __________
1 | abcxyz                       |    5
2 | abcdefghijklmnopqrstuvwxyz   |   25
3 | aaaaaaaaa                    |    0
4 | zzzzzzzzz                    |    1
5 | zzzzbzzzz                    |    3

Case 1, target abcxyz:

aaa[aaa] -> DOWN -> aaazzz
aaa[zz]z -> DOWN -> aaayyz
aaa[y]yz -> DOWN -> aaaxyz
a[aa]xyz ->  UP  -> abbxyz
ab[b]xyz ->  UP  -> abcxyz

Case 5, target zzzzbzzzz:

[aaaaaaaaa] -> DOWN -> zzzzzzzzz
zzzz[z]zzzz ->  UP  -> zzzzazzzz
zzzz[a]zzzz ->  UP  -> zzzzbzzzz
Pungent answered 11/6, 2013 at 20:55 Comment(15)
it's not clear what you want? Are you looking for an algorithm?Housekeeping
Yes. I need a algorithm.Pungent
I think OP is looking for an algorithm to tell the minimum number of moves from all "a"s to the input configuration. So if your input configuration is "aac" then it would return 2 because you would start at "aaa" --> "aab" --> "aac". Is this correct, @LeonardoApolinário?Mindi
Why is zzzzbzzzz three moves? Can't you do aaaaaaaaa => zzzzzzzzz => zzzzbzzzz (2 moves)?Johnathanjohnathon
@JanDvorak: I think that middle z goes back to a first.Phytology
@JanDvorak or one wheel can be turned only once.Glabrescent
@Phytology as in, you can't rotate directly from z to b in one move, even if the wheel is alone?Johnathanjohnathon
@JanDvorak: Yes, from the examples, it seems a move consists of single line of rotation, where a line can be one or more contiguous wheels. abc => bcd is one move.Phytology
@Phytology in which case, how is aaaaaaaaa=>zzzzzzzzz=>zzzzbzzzz invalid?Johnathanjohnathon
@JanDvorak: It's not, you just skipped a step: aaaaaaaaa => zzzzzzzzz => zzzzazzzz => zzzzbzzzz.Phytology
@Phytology why should zzzzzzzzz=>zzzzazzzz=>zzzzbzzzz count as two separate steps?Johnathanjohnathon
@JanDvorak: Because from the description above, a move consists of a transition to the current value's successor or predecessor.Phytology
@JanDvorak Check the sample inputs and outputs. It is correct. They are all special cases. There should be a case "ada"Glabrescent
@LeonardoApolinário In order to clarify your question, I've made some edits, and added some move examples, considering the comments above. Please, if something is incorrect, edit it.Cassius
I think this question is constructive enough for being reopened. It is, yes, a programming question, properly tagged as algorithm, and, although its text was a bit confusing at the beginning, I do really think its current version is minimally well-stated to accept answers.Cassius
C
4

This problem may be restated as:

What is the minimum number of moves to turn a string S, into a string that only contains 'a'?

Definition:

Consider a contiguous subsequence as a sequence of equal characters in the string. The smallest contiguous subsequence is, naturally, a single character. If you normalize small subsequences, you'll, naturally, end up with bigger subsequences, eventually reaching a single subsequence -- the entire string.

What to normalize to:

One can only move a character UP or DOWN, so, a character itself is a sequence of UP and DOWN moves. The worst case of representation of a character is the letter in the middle of the alphabet, which requires at least len(alphabet) / 2 moves to be described. In the alphabet {a..z}, the worst cases are 'm' and 'n'.

Since we want to minimize the number of moves, we need to pull DOWN letters C <= m, and pull UP those C >= n. Thus, to minimize the normalization process, we must find the greatest subsequences that requires equal normalization moves. For example, if we have a target zzzzbzzzz, we know the minimal directions are UUUUDUUUU -- U for UP, and D, DOWN.

Normalizing:

For each move, the counter is incremented, yielding the least number of moves required to transform a string. Considering the above example, we may take the following steps:

# = 0 | zzzzbzzzz | UUUUDUUUU  (choose the smallest subsequence to normalize)
# = 1 | zzzzazzzz | UUUUDUUUU  (since 'a' is the base character, we choose
                              the move that increases the largest subsequence;
                              if 'a' was the first or last character,
                              moving it would simply be overhead)
# = 2 | zzzzzzzzz | UUUUUUUUU  (choose the subsequence to normalize)
# = 3 | aaaaaaaaa | _________  (choose the subsequence to normalize)

Another example, with the target string abcxyz:

# = 0 | abcxyz | _DDUUU  (choose the smallest subsequence to normalize)
# = 1 | aabxyz | __DUUU  (choose the smallest subsequence to normalize)
# = 2 | aaaxyz | ___UUU  (choose the smallest subsequence to normalize)
# = 3 | aaayza | ___UU_  (choose the smallest subsequence to normalize)
# = 4 | aaazaa | ___U__  (choose the smallest subsequence to normalize)
# = 5 | aaaaaa | ______  (choose the smallest subsequence to normalize)

EDIT:

As pointed by @user1884905, this solution, as it is proposed, is not optimal. In the case of a target string mn, the algorithm does not lead to an optimal solution:

# = 0  | mn | DU  (choose the smallest subsequence to normalize)
# = 1  | ln | DU  (choose the smallest subsequence to normalize)
# = 2  | kn | DU  (choose the smallest subsequence to normalize)
...
# = 12 | an | _U  (choose the smallest subsequence to normalize)
# = 13 | ao | _U  (choose the smallest subsequence to normalize)
# = 14 | ap | _U  (choose the smallest subsequence to normalize)
...
# = 24 | aa | __  (choose the smallest subsequence to normalize)

And this is not optimal, as the following steps require less moves:

#0    #1    #2    ...    #12
mn -> mm -> ll -> ... -> aa

Maybe the optimal substructure to a greedy algorithm lies in reducing the global distance between the characters from the string, instead of focusing on the difference between such characters and the base case ('a').

Cassius answered 11/6, 2013 at 23:15 Comment(10)
Nice answer! I have a question: Is there any situation in which a wheel must be rotated to the opposite direction? for example: when a d should become an e to solve the problem with minimal steps. Or, I think this is the same question: if a wheel shows a, is there a situation when we should change it? Your first example can be solved in 3 steps when You rotate the zzzz sequences instead of rotating a to z. I try to make a tricky target string later and post it if You don't mind.Glabrescent
@gkovacs90 I don't mind it at all; I'm actually trying to think of some corner case in which this approach fails. In the first example you pointed, both rotating a, and later all the z's, or rotating the two chunks of z's (two moves), and later b to a (one move), will take three steps. Since the result is the same, this is a situation when it's necessary to rotate a to something, to minimize the steps. Notice this is a O(n^2) greedy solution, that always tries to maximize inner subsequences, in order to minimize the number of moves employed.Cassius
@Cassius Thank You. I will try to implement it recursively and save the results in a table, to meet the requirements of dynamic programming. By the way, we studied in the same college.Pungent
@Cassius You explained a situation where rotating a to something is not necessary, because there is an other way when we don't change any a and the result is the same. Please check my semi-answer.Glabrescent
In your zzzzbzzzz example I don't see why you moved zzzzazzzz to zzzzzzzzz. I understand that from there you can move the whole string to as in one move but that is more complicated logic. I thought that once a character is in the right place you shouldn't touch it which means zzzzbzzzz -> zzzzazzzz -> aaaaazzzz -> aaaaaaaaa would be the sequence of operations which also requires 3 moves. This logic is simpler and still gets you to the answer optimally (even if the z were something else, like w both methods would take the same number of moves - 9)Attah
@Cassius I like that you turned the problem around and considered the problem of turning a string into a's, it makes it easier to think about. However, dividing the alphabet into two parts and turning the lower part down and the upper part up will not always produce optimal solutions. Think of strings with letters close to the middle of the alphabet. As a simple example we might consider the string mn, here it is clear that we should not turn m upwards and n downwards, we should rather combine them using one move and then move them both together back to aa.Tube
@GuyGreer +1 The reason was simply the policy I chose to reduce the problem iteratively. I guess this was not the best alternative, though, as it seems to yield the same result of never messing with a correct char.Cassius
@Tube +1 Perfect! There are corner cases, and this solution is not optimal. I'll try to improve this approach somehow, and I guess gkovacks90 is in the right direction. Thanks for pointing out such example!Cassius
@LeonardoApolinário could You provide more correct test cases?Glabrescent
@gkovacs90 I only have this cases, sorry.Pungent
G
2

Since this is just some additional info and maybe some optimization, this should be a comment to the answer of Rubens, but in an answer I can explain it better and it can be useful for the questioner too.

I also use the great reverse idea of Rubens.

So, I think there's no situation when it is necessary to rotate an a to something else. If this is correct (I have no counterexample), there is no situation where we should rotate something to the wrong direction ( I have no mathematican proof, but probably this is correct).

So, every subsequences of Us and Ds will be rotated each time with one motion. This algorithm won't take O(n^2) time. Here is the algorithm:

Let we call Rubens's string direction string

  1. set a counter to 0
  2. compute the direction string
  3. scan the direction string.
  4. if You find a contigous subsequence of U or D letters, rotate the target string at the positions towards a and increment the counter(once for each subsequence).
  5. if there was any operation, go back to step 2

This algorithm will rotate every wheel to a and it will be done after at most k/2 scanning, where k is the count of the elements of the alphabet, so this may be a solution that runs in linear time.


Maybe there is a solution with even less operation. It is just an idea, with finding increasing, decreasing or "hill-shape" sub-subsequences and extract the maximum value.For example: We can say without computing, that the cost of solving

  • abcde,
  • ecb,
  • abceeddcb

is equal to the cost of solving a single e

EDIT: I've seen user1884905's counterexample. So my algorithm will not find the optimal solution, but it can be usable to find the correct algorithm, so I don't delete it yet.

EDIT 2: Another idea that works with the sample target strings: There could be computed an average letter. It is the one for which the sum of the distances from the target string's letters is minimal. Every letter should be rotated here with the algorithm above, then the whole string can be rotated to aaaaaaaaaa. Since the alphabet is cyclic, there could be more than one average letter( like in the second example in the question), in this case we should chose the one with the minimal distance from a.

Glabrescent answered 12/6, 2013 at 12:35 Comment(5)
As from the comments in the answer I posted, there are corner cases in my approach (case mn), when following the steps I proposed does not lead to the optimal solution. Try to run this case, and some other example with your algorithm, so we may have a correct answer. As for the second part, I was actually trying to reach some of those overlapping cases with that normalization process, in order to make the solution dynamic.Cassius
@Cassius I'm thinking of turning as much as possible in a certain direction and calculate the number of turns of that piece. For example, abcxyz, bc I would turn up and then down xyz. I was thinking of using quicksort or other algorithm to sort the sequence that I have to turn up or down and store the calculated values ​​in a table. That would be dynamic programming? A subproblem would be trying to rotate as much as possible in one direction?Pungent
@LeonardoApolinário In dynamic programming, a subproblem is usually some computation you have already performed, and that you don't need to perform again, as you already have its result. I didn't really get your idea: wouldn't you break the initial order restraint sorting the sequence?Cassius
I found this explanation about dynamic programming, http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=dynProg. I'm thinking of solving by analogy. Instead of coins, I'm thinking the number of spins up or down. I do not know if it's right, but i will try.Pungent
@gkovacs90 The greater rotation to UP or DOWN can be interpreted as a optimal substructure?Pungent
T
1

A rigorous (and surprisingly involved) solution of this problem can now be found in the preprint available at arxiv.org/abs/2308.10321 which will appear in the American Mathematical Monthly. It is written with a broad mathematical audience in mind but I want to restate the core aspects using only minimal notation.

For this purpose, let me restate the problem. Identify each of the letters of the alphabet with their position, starting with zero, i.e., a <-> 0, b <-> 1,... Assume we are given a combination lock with n wheels numbered from left to right by 1,2,...,n and on each wheel the numbers from 0 to N-1=25 are printed. In each move we are allowed to turn an interval (a contiguous subsequence) of wheels, e.g. wheels numbered either upwards or downwards.

What is the minimal number of moves to get from an initial combination f = (f(1),...,f(n)) to a target combination g = (g(1),...,g(n))?

Here, f(i) is in [0,N] := {0,...,N} is the number chosen on the i-th wheel. By rotating both initial and target in advance, we can assume that g = (0,...,0). The optimal number of moves necessary to turn f into g can then be written in terms of the forward difference

Df(i) = f(i+1) - f(i) for i = 0,...,n,

where we extend f by setting f(0) = f(n+1) = 0. Let

D_+ = {i in [0,n]: Df(i) > 0} and D_- = {i in [0,n]: Df(i) < 0}

be the subsets where f has an upward/downward jump and enumerate them nonincreasingly according to the size of the jump, i.e.,

D_+ = {i_1,...,i_L} such that |Df(i_1)| >= ... >= |Df(i_L)|,

and

D_- = {j_1,...,j_M} such that |Df(j_1)| >= ... >= |Df(j_M)|,

where L is the length of D_+ and M is the length of D_-. Then the minimal number of moves is given by

A - B,

where

A = (1/2)*(|Df(0)| + |Df(1)| + ... + |Df(n)|)

and

B = max{(|Df(i_1)| + |Df(j_1)| - N), 0} + ... + max{(|Df(i_K)| + |Df(j_K)| - N), 0},

where K is the minimum of L and M. If L or M are equal to zero, then we set B = 0.

That is, the minimal number of moves is half of the total height of jumps in the combination, augmented by zeros at each end, minus some compensation which is given by pairs of large upward and downward jumps which in total exceed the 'height' N of the lock.

Consider for example a wildly varying combination such as f = (0,N-1,0,N-1,...,N-1) (which is extended to (0,0,N-1,0,N-1,...,N-1,0) ) and assume that n is even. Then the optimal way to turn it into the zero combination is to turn each of the N-1's upwards to a zero, taking n/2 moves. There are n/2 upward jumps of height N-1 and n/2 downward jumps of height N-1. The above formula gives A = (N-1)n/2 and for B=(n/2)(N-2) resulting in A-B=n/2. This is exactly the same as there are many large upward and downward jumps which 'cancel' each other.

On the other hand a monotone combination such as f = (0,1,2,3,4,...,25) (which is extended to (0,0,1,2,3,4,...,25,0) ) will have only one large downward jump of height 25 but no large upward jump to compensate it. Therefore, the total sum of jumps is 25+25 giving 25 as the least number of moves.

The proof of the formula for the optimal number of moves can be turned into an algorithm to give an optimal sequence of moves. In short, one shifts the combination at large jumps and then 'fills' it with blocks. Since the process is somewhat involved I refer to the above preprint for more explanation.

Ting answered 5/12, 2023 at 8:45 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Vinificator

© 2022 - 2024 — McMap. All rights reserved.