I have some Morse code that has lost the spaces in between the letters, my challenge is to find out what the message says. So far I have been kinda lost because of the sheer amount of combinations there might be.
Here is all the info on the messages I have.
The output will be English
There will always be a translation that make sense
Here is and example message -..-...-...-...-..-.-.-.-.-..-.-.-.-.-.-.-.-.-.-..-...-.
The messages should be no longer then 70 characters
The morse code was taken from a longer stream so it is possible that the first or last groups may be cut off and hence have no valid translation
Does anyone have a clever solution?
This is not an easy problem, because as ruakh suggested there are many viable sentences to a given message. For example 'JACK AND JILL WENT UP THE HILL' has the same encoding as 'JACK AND JILL WALK CHISELED'. Since these are both grammatical sentences and the words in each are common, it's not obvious how to pick one or the other (or any other of the 40141055989476564163599 different sequences of English words that have the same encoding as this message) without delving into natural language processing.
Anyway, here's a dynamic programming solution to the problem of finding the shortest sentence (with the fewest characters if there's a tie). It can also count the total number of sentences that have the same encoding as the given message. It needs a dictionary of English words in a file.
The next enhancements should be a better measure of how likely a sentence is: perhaps word frequencies, false-positive rates in morse (eg, "I" is a common word, but it appears often as part of other sequences of morse code sequences). The tricky part will be formulating a good score function that can be expressed in a way that it can be computed using dynamic programming.
MORSE = dict(zip('ABCDEFGHIJKLMNOPQRSTUVWXYZ', [
'.-', '-...', '-.-.', '-..', '.', '..-.', '--.', '....',
'..', '.---', '-.-', '.-..', '--', '-.', '---', '.--.',
'--.-', '.-.', '...', '-', '..-', '...-', '.--', '-..-',
'-.--', '--..'
]))
# Read a file containing A-Z only English words, one per line.
WORDS = set(word.strip().upper() for word in open('dict.en').readlines())
# A set of all possible prefixes of English words.
PREFIXES = set(word[:j+1] for word in WORDS for j in xrange(len(word)))
def translate(msg, c_sep=' ', w_sep=' / '):
"""Turn a message (all-caps space-separated words) into morse code."""
return w_sep.join(c_sep.join(MORSE[c] for c in word)
for word in msg.split(' '))
def encode(msg):
"""Turn a message into timing-less morse code."""
return translate(msg, '', '')
def c_trans(morse):
"""Construct a map of char transitions.
The return value is a dict, mapping indexes into the morse code stream
to a dict of possible characters at that location to where they would go
in the stream. Transitions that lead to dead-ends are omitted.
"""
result = [{} for i in xrange(len(morse))]
for i_ in xrange(len(morse)):
i = len(morse) - i_ - 1
for c, m in MORSE.iteritems():
if i + len(m) < len(morse) and not result[i + len(m)]:
continue
if morse[i:i+len(m)] != m: continue
result[i][c] = i + len(m)
return result
def find_words(ctr, i, prefix=''):
"""Find all legal words starting from position i.
We generate all possible words starting from position i in the
morse code stream, assuming we already have the given prefix.
ctr is a char transition dict, as produced by c_trans.
"""
if prefix in WORDS:
yield prefix, i
if i == len(ctr): return
for c, j in ctr[i].iteritems():
if prefix + c in PREFIXES:
for w, j2 in find_words(ctr, j, prefix + c):
yield w, j2
def w_trans(ctr):
"""Like c_trans, but produce a word transition map."""
result = [{} for i in xrange(len(ctr))]
for i_ in xrange(len(ctr)):
i = len(ctr) - i_ - 1
for w, j in find_words(ctr, i):
if j < len(result) and not result[j]:
continue
result[i][w] = j
return result
def shortest_sentence(wt):
"""Given a word transition map, find the shortest possible sentence.
We find the sentence that uses the entire morse code stream, and has
the fewest number of words. If there are multiple sentences that
satisfy this, we return the one that uses the smallest number of
characters.
"""
result = [-1 for _ in xrange(len(wt))] + [0]
words = [None] * len(wt)
for i_ in xrange(len(wt)):
i = len(wt) - i_ - 1
for w, j in wt[i].iteritems():
if result[j] == -1: continue
if result[i] == -1 or result[j] + 1 + len(w) / 30.0 < result[i]:
result[i] = result[j] + 1 + len(w) / 30.0
words[i] = w
i = 0
result = []
while i < len(wt):
result.append(words[i])
i = wt[i][words[i]]
return result
def sentence_count(wt):
result = [0] * len(wt) + [1]
for i_ in xrange(len(wt)):
i = len(wt) - i_ - 1
for j in wt[i].itervalues():
result[i] += result[j]
return result[0]
msg = 'JACK AND JILL WENT UP THE HILL'
print sentence_count(w_trans(c_trans(encode(msg))))
print shortest_sentence(w_trans(c_trans(encode(msg))))
I don't know if this is "clever", but I would try a breadth-first search (as opposed to the depth-first search implicit in BRPocock's regex idea). Suppose your string looks like this:
.---.--.-.-.-.--.-...---...-...-..
J A C K A N D J I L L
You start out in state ('', 0) ('' being what you've decoded so far; 0 being your position in the Morse-code string). Starting from position zero, possible initial characters are . E, .- A, .-- W, .--- J, and .---- 1. So, push states ('E', 1), ('A', 2), ('W', 3), ('J', 4), and ('1', 5) onto your queue. After dequeuing state ('E', 1), you would enqueue states ('ET', 2), ('EM', 3), and ('EO', 4).
Now, your queue of possible states will grow quite quickly — both of { ., - } are letters, as are all of { .., .-, -., -- } and all of { ..., ..-, .-., .--, -.., -.-, --., --- }, so in each pass your number of states will increase by a factor of at least three — so you need to have some mechanism for user feedback. In particular, you need some way to ask your user "Is it plausible that this string starts with EOS3AIOSF?", and if the user says "no", you will need to discard state ("EOS3AIOSF", 26) from your queue. The ideal would be to present the user with a GUI that, every so often, shows all current states and lets him/her select which ones are worth proceeding with. ("The user" will also be you, of course. English has a shortage of pronouns: if "you" refers to the program, then what pronoun refers to the user-programmer?!)
Maintain 3 things: a list of words so far S, the current word so far W, and the current symbol C.
S should be only good words, eg. 'THE QUICK'
W should be a valid prefix of a word, eg. ['BRO']
C should be a valid prefix of some letter, eg. '.-'
Now, given a new symbol, let's say '-', we extend C with it (in this case we get '.--').
If C is a complete letter (in this case it is, the letter 'W'), we have a choice to add it to W, or to continue extending the letter further by adding more symbols.
If we extend W, we have a choice to add it to S (if it's a valid word), or to continue extending it further.
This is a search, but most paths terminate quickly (as soon as you have W not being a valid prefix of any word you can stop, and as soon as C isn't a prefix of any letter you can stop).
To get it more efficient, you could use dynamic programming to avoid redundant work and use tries to efficiently test prefixes.
What might the code look like? Omitting the functions 'is_word' which tests if a string is an English word, and 'is_word_prefix' which tests if a string is the start of any valid word, something like this:
morse = {
'.-': 'A',
'-...': 'B',
etc.
}
def is_morse_prefix(C):
return any(k.startswith(C) for k in morse)
def break_words(input, S, W, C):
while True:
if not input:
if W == C == '':
yield S
return
i, input = input[0], input[1:]
C += i
if not is_morse_prefix(C):
return
ch = morse.get(C, None)
if ch is None or not is_word_prefix(W + ch):
continue
for result in break_words(input, S, W + ch, ''):
yield result
if is_word(W + ch):
for result in break_words(input, S + ' ' + W + ch, '', ''):
yield result
for S in break_words('....--', [], '', ''):
print S
Related
How the game works is that there is a 3-digit number, and you have to guess it. If you guess a digit in the right spot, you get a strike, and if you guess a digit but in the wrong spot you get a ball. I've coded it like this.
x = random.randint(1, 9)
y = random.randint(1, 9)
z = random.randint(1, 9)
userguessunlisted = input('What number do you want to guess?')
numbertoguess = list[x, y, z]
userguess = list(userguessunlisted)
b = 0
s = 0
while 0 == 0:
if userguess[0] == numbertoguess[0]:
s = s + 1
if userguess[0] == numbertoguess[1]:
b = b + 1
if userguess[0] == numbertoguess[2]:
b = b + 1
if userguess[1] == numbertoguess[0]:
b = b + 1
if userguess[1] == numbertoguess[1]:
s = s + 1
if userguess[1] == numbertoguess[2]:
b = b + 1
if userguess[2] == numbertoguess[0]:
b = b + 1
if userguess[2] == numbertoguess[1]:
b = b + 1
if userguess[2] == numbertoguess[2]:
s = s + 1
print(s + "S", b + "B")
if s != 3:
b = 0
s = 0
else:
print('you win!')
break
When you said list[x, y, z] on line 5, you used square brackets, which python interprets to be a type annotation. For example, if I wanted to specify that a variable is a list of ints, I could say
my_list_of_ints: list[int] = [1, 2, 3]
I think what you meant to do is create a new list from x, y, and z. One way to do this is
numbertoguess = list([x, y, z])
which is probably what you meant to write. This is valid because the list function takes an iterable as its one and only argument.
However, the list portion is redundant; square brackets on the right-hand side of an assignment statement already means "create a list with this content," so instead you should simply say
numbertoguess = [x, y, z]
A few other notes:
input will return a string, but you are comparing that string to integers further down, so none of the comparisons will ever be true. What you want to say is something like the following:
while True:
try:
userguessunlisted = int(input('What number do you want to guess?'))
except:
continue
break
What this code does is attempts to parse the string returned from input into an int. If it fails to do so, which would happen if the user inputted something other than a valid integer, an exception would be thrown, and the except block would be entered. continue means go to the top of the loop, so the input line runs repeatedly until a valid int is entered. When that happens, the except block is skipped, so break runs, which means "exit the loop."
userguessunlisted is only ever going to contain 1 number as written, so userguess will be a list of length 1, and all of the comparisons using userguess[1] and userguess[2] will throw an IndexError. Try to figure out how to wrap the code from (1) in another loop to gather multiple guesses from the user. Hint: use a for loop with range.
It might also be that you meant for the user to input a 3-digit number all at once. In that case, you can use a list comprehension to grab each character from the input and parse it into a separate int. This is probably a bit complicated for a beginner, so I'll help you out:
[int(char) for char in input('What number do you want to guess?')]
print(s + "S", b + "B") will throw TypeError: unsupported operand type(s) for +: 'int' and 'str'. There are lots of ways to combine non-string types with strings, but the most modern way is using f-strings. For example, to combine s with "S", you can say f"{s}S".
When adding some amount to a variable, instead of saying e.g. b = b + 1, you can use the += operator to more concisely say b += 1.
It's idiomatic in python to use snake_case for variables and Pascal case for classes. So instead of writing e.g. numbertoguess, you should use number_to_guess. This makes your code more readable and familiar to other python programmers.
Happy coding!
I am trying to add a constraint which specifies that, in the optimization, the solver must pick a value of u for a set duration of time and can only switch after that set amount of time. For instance, say I have a mechanical device which can only switch its input value every 10 seconds. Then, I want the optimizer to account for that. I'll just attach the code here:
for it_i in range(0, N-1, equivalence_samples):
print("N: {}".format(N))
for it_j in range(0, equivalence_samples - 1):
if (it_i + it_j + 1) > N-1:
print("Breaking")
break
else:
constraint_u0 = prog.AddConstraint(u[0, it_i + it_j] == u[0, it_i + it_j + 1]) # add equivalence constraints
constraint_u1 = prog.AddConstraint(u[1, it_i + it_j] == u[1, it_i + it_j + 1]) # add equivalence constraints
print('Constraint_u_PE: {}'.format(constraint_u0))
print('Constraint_u_NI: {}'.format(constraint_u1))
I have implemented this in, what I expect to be a working solution. Sometimes it seems like it is working and other times, it does not.
I will show some photos of the output constraints from this and then a not working example.
Then, here are the plots that come out which clearly show there is some delineation between the switching times, but the values are not equivalent. I am attaching the code which generates this plot as well.
u_sol = result.GetSolution(u)
u_time = np.linspace(0, N-1, num = N)
# u_sol_trajectory = PiecewisePolynomial.ZeroOrderHold(u_time, u_sol)
plt.figure()
plt.plot(u_time, u_sol[0, :], 'o')
plt.plot(u_time, u_sol[1, :], 'o')
plt.xlabel('time steps')
plt.ylabel('u [mcg/min]')
plt.legend(['u_PE', 'u_NI'])
The particular solver that was being used in this case is the OSQP solver. Although I, in an ideal solver world, specified the correct constraints in the above code (that input 1 == input 2, input 2 == input 3, etc.), I did not account for the fact that solvers have an accuracy with which they try to uphold the constraints.
I can fix this problem by either updating the accuracy of the solver (as recommended by https://osqp.discourse.group/t/constraints-violated/143) or inputing more explicit constraints. I solved this with the second option. Now, I am specifying not just constraints like the following pattern:
input 1 == input 2, input 2 == input 3, etc.
but I am also including constraints like the following pattern:
input 1 == input 3, input 1 == input 4, input 1 == input 5
input 2 == input 4, input 2 == input 5 etc.
By being more explicit, my solver is now doing what I asked with small deviations from the constraint. The small deviations are acceptable for my application, however! It is a bit slower, but this isn't a problem for what I am using this for at the moment. Here is my updated code:
for it_i in range(0, N-1, equivalence_samples):
for it_j in range(0, equivalence_samples - 1):
for it_f in range(1, equivalence_samples - it_j):
if (it_i + it_j + it_f) > N-1:
print("Breaking")
break
else:
con_0 = eq(u[:, it_i + it_j], u[:, it_i + it_j + it_f])
constraint_u = prog.AddConstraint(con_0) # add equivalence constraints
print('Constraint_u: {}'.format(constraint_u))
Not the prettiest code in the world, but it works.
I saw this pseudo-code on another stackoverflow question found here Split a string to a string of valid words using Dynamic Programming.
The problem is a dynamic programming question to see if an input string can be split into words from a dictionary.
The third line, means to set an array b of size [N+1] to all false values? I'm pretty sure about that. But what I am really not sure about is the fifth line. Is that a for-loop or what? I feel like pseudo-code saying 'for i in range' would only have 2 values. What is that line saying?
def try_to_split(doc):
N = len(doc)
b = [False] * (N + 1)
b[N] = True
for i in range(N - 1, -1, -1):
for word starting at position i:
if b[i + len(word)]:
b[i] = True
break
return b
It's confusing syntax, and I'm pretty sure there's a mistake. It should be:
for i in range(N - 1, 0, -1) //0, not -1
which I believe means
for i from (N - 1) downto 0 //-1 was the step, like i-- or i -= 1
This makes sense with the algorithm, as it simply starts at the end of the string, and solves each trailing substring until it gets to the beginning. If b[0] is true at the end, then the input string can be split into words from the dictionary. for word starting at position i just checks all words in the dictionary to see if they start at that position.
If one wants to be able to reconstruct a solution, they can change b to an int array, initialize to 0s, and change the if to this:
if b[i + len(word)] != 0
b[i] = i + len(word) //len(word) works too
break
I've coded a basic Mandelbrot explorer in C#, but I have those horrible bands of color, and it's all greyscale.
I have the equation for smooth coloring:
mu = N + 1 - log (log |Z(N)|) / log 2
Where N is the escape count, and |Z(N)| is the modulus of the complex number after the value has escaped, it's this value which I'm unsure of.
My code is based off the pseudo code given on the wikipedia page: http://en.wikipedia.org/wiki/Mandelbrot_set#For_programmers
The complex number is represented by the real values x and y, using this method, how would I calculate the value of |Z(N)| ?
|Z(N)| means the distance to the origin, so you can calculate it via sqrt(x*x + y*y).
If you run into an error with the logarithm: Check the iterations before. If it's part of the Mandelbrot set (iteration = max_iteration), the first logarithm will result 0 and the second will raise an error.
So just add this snippet instead of your old return code. .
if (i < iterations)
{
return i + 1 - Math.Log(Math.Log(Math.Sqrt(x * x + y * y))) / Math.Log(2);
}
return i;
Later, you should divide i by the max_iterations and multiply it with 255. This will give you a nice rgb-value.
I have this number x and i wanted to find all numbers which are relatively prime to it.
my code so far:
For i = 1 To x-1
if [number n is relatively prime to x] Then
ListBox1.Items.Add(x)
End If
Next
Thanks in advance
Two numbers are relatively prime if their greatest common divisor is 1. VB doesn't have the GCD function built-in, but the algorithm is simple enough (and about 2300 years old!):
function gcd(m, n)
while n > 0
m, n = n, m%n
return m
Note that m and n are assigned simultaneously. I'll leave it to you to complete the VB implementation. You might be interested in googling for the totient of a number and the list of its totatives, which is what you are calculating.
Assuming you want only numbers that are smaller than x, which are coprime with it - you could also take a generative approach, running a special kind of a sieve. When the multiples of each prime are generated, you'd see if that sequence "hits" your upper limit x or misses it, and mark all the numbers in it as non-coprimes if it does hit x.
Or in "pseudocode" (with Haskell syntax :) ),
coprimes n = go( [1..n-1], [2..n-1]) where
go( xs, [] ) = xs -- ' no more numbers to sieve - return xs
go( xs, p:ks ) = -- ' p is first in candidates, ks is the rest
let ms = [p, 2*p .. n-1] -- ' p's multiples
in
go( if ( (mod n p) == 0 ) -- ' is n a multiple of p ?
then (xs\\ms) -- ' yes: remove p's multiples
else xs, -- ' no: possible coprimes
ks\\ms ) -- ' candidates to sieve
Haskell's set difference \\ is very inefficient with unordered list representation of sets, but you would naturally encode this efficiently, on top of mutable arrays, in VB.