Compact way to index from x to y in 2D list - arrays

I am doing the tictactoe project from the Cisco NetAkad course : https://www.youtube.com/watch?v=7GDtI9SpGgU
I did the game disregarding the stated project requirements.
I used for the board a normal list: board = [1, 2, 3, 4, 5, 6, 7, 8, 9]
The programm works and and the output is the same as the project asks for.
The project requirements state however specifically to use a 2D list: board = [[1, 2, 3], [4, 5, 6], [7, 8, 9]. As I wanted to practise using 2D lists I rewrote the programm and found it requires more lines and more complex indexing.
My 1st question is: in the function, which checks whether there is a winner, is it possible with a 2D list to just use indexing to check whether there are 3 consecutive "X" or "O" for all directions? If yes, how?
(I don't think writing the massive amount of 'if' combined with many 'and', like in the video, is efficient and using indexing works well with a normal list)
As a beginner my wording might be unclear, so here is the section checking whether "X" has won and below what I expect it to do:
if board[0][0:3].count("X") == 3 or board[1][0:3].count("X") == 3 \
or board[2][0:3].count("X") == 3 or board[0:3][0].count("X") == 3 \
or board[0:3][1].count("X") == 3 or board[0:3][2].count("X") == 3 \
or board[0:3][0:3].count("X") == 3 or board[0:3][3::-1].count("X") == 3:
print("I won: you lost.")
The index [0][0:3], [1][0:3] and [2][0:3] work as expected (rows), it recognises the win.
However [0:3][0] is being read as [0][0:3] when the programm is run. (checking colums doesn't work)
[0:3][0:3] and [0:3][3::-1] obviously doesn't work (diagonal).
2nd question: What better way is there to check for a winner?
Last question: Is there an advantage of using a 2D list over a normal list in this case?
I am thankful in advance for any feedback.

You can do something like below:
for key in ['X', 'O']:
temp = []
count = 0
for row in board:
if row.count(key) == 3:
count = 3
break
if key in row:
temp.append(row.index(key))
if count == 3 or (len(list(set(temp))) == 1 and len(temp) == 3) or temp in ([0,1,2], [2,1,0]):
print("I won: you lost. " + key + " wins")
The idea of the solution is to get the indexes of the positions of 'X' in the inner lists (the usage of indexes justifies the reason to go for a '2D list' [list of lists] per your last question). This is done by looping over the list per 'row' of the tic-tac-toe. We store the indexes in the variable 'temp'.
The first condition checks whether a row has 3 'X's and if yes, it breaks the execution of the for loop saving a count=3. The second condition is the most interesting because it stores the index of 'X' in the row. For example, when you have X in the middle column, at the end of the loop, temp will be equal to:
[2,2,2] if the tic-tac-toe was
O-X-
O-X-O
-X-O
Consequently, if there is a single unique number in the 'temp' list ([1,1,1] unique is 1, [2,2,2] unique is 2 etc), then there is a winner. This is done by:
len(list(set(temp))) # (inside) Get unique values / Make it a list / Measure its length
If the length is 1, then there is a unique position. In addition to that, and to account for the code running while the tic-tac-toe is not entirely filled, we check for len(temp) == 3. Finally, we check for a diagonal match using the 'temp in ([0,1,2], [2,1,0])' part of the condition
The reason why the 'index [0:3][0]' does not work as you expect it to, is because the first slice points to the entire list for which you are taking the value at position 0 which is [1,2,3]. In other words board[0:3] is equal to board

Since there are so few potential states and only 8 winning states, I might just go directly with evaluating them all:
gameboard = [
["x","-","-"],
["x","-","-"],
["x","-","-"]
]
possilbe_winning_states = [
[(0,0), (0,1), (0,2)],
[(1,0), (1,1), (1,2)],
[(2,0), (2,1), (2,2)],
[(0,0), (1,0), (2,0)],
[(0,1), (1,1), (2,1)],
[(0,2), (1,2), (2,2)],
[(0,0), (1,1), (2,2)],
[(0,2), (1,1), (2,0)]
]
for possilbe_winning_state in possilbe_winning_states:
squares = [gameboard[square[0]][square[1]] for square in possilbe_winning_state]
if squares.count("x") == 3:
print("X Win")
break
elif squares.count("o") == 3:
print("O Win")
break
else:
print("no winner yet")

Related

algorithm which finds the numbers in a sequence which appear 3 times or more, and prints their indexes

Suppose I input a sequence of numbers which ends with -1.
I want to print all the values of the sequence that occur in it 3 times or more, and also print their indexes in the sequence.
For example , if the input is : 2 3 4 2 2 5 2 4 3 4 2 -1
so the expected output in that case is :
2: 0 3 4 6 10
4: 2 7 9
First I thought of using quick-sort , but then I realized that as a result I will lose the original indexes of the sequence. I also have been thinking of using count, but that sequence has no given range of numbers - so maybe count will be no good in that case.
Now I wonder if I might use an array of pointers (but how?)
Do you have any suggestions or tips for an algorithm with time complexity O(nlogn) for that ? It would be very appreciated.
Keep it simple!
The easiest way would be to scan the sequence and count the number of occurrence of each element, put the elements that match the condition in an auxiliary array.
Then, for each element in the auxiliary array, scan the sequence again and print out the indices.
First of all, sorry for my bad english (It's not my language) I'll try my best.
So similar to what #vvigilante told, here is an algorithm implemented in python (it is in python because is more similar to pseudo code, so you can translate it to any language you want, and moreover I add a lot of comment... hope you get it!)
from typing import Dict, List
def three_or_more( input_arr:int ) -> None:
indexes: Dict[int, List[int]] = {}
#scan the array
i:int
for i in range(0, len(input_arr)-1):
#create list for the number in position i
# (if it doesn't exist)
#and append the number
indexes.setdefault(input_arr[i],[]).append(i)
#for each key in the dictionary
n:int
for n in indexes.keys():
#if the number of element for that key is >= 3
if len(indexes[n]) >= 3:
#print the key
print("%d: "%(n), end='')
#print each element int the current key
el:int
for el in indexes[n]:
print("%d,"%(el), end='')
#new line
print("\n", end='')
#call the function
three_or_more([2, 3, 4, 2, 2, 5, 2, 4, 3, 4, 2, -1])
Complexity:
The first loop scan the input array = O(N).
The second one check for any number (digit) in the array,
since they are <= N (you can not have more number than element), so it is O(numbers) the complexity is O(N).
The loop inside the loop go through all indexes corresponding to the current number...
the complexity seem to be O(N) int the worst case (but it is not)
So the complexity would be O(N) + O(N)*O(N) = O(N^2)
but remember that the two nest loop can at least print all N indexes, and since the indexes are not repeated the complexity of them is O(N)...
So O(N)+O(N) ~= O(N)
Speaking about memory it is O(N) for the input array + O(N) for the dictionary (because it contain all N indexes) ~= O(N).
Well if you do it in c++ remember that maps are way slower than array, so if N is small, you should use an array of array (or std::vector> ), else you can also try an unordered map that use hashes
P.S. Remember that get the size of a vector is O(1) time because it is a difference of pointers!
Starting with a sorted list is a good idea.
You could create a second array of original indices and duplicate all of the memory moves for the sort on the indices array. Then checking for triplicates is trivial and only requires sort + 1 traversal.

Find missing number in an array?

A friend of mine asked this question long time ago. He asked me to this without iterating the array. I had in my mind to find the sum on N number as then subtract from it the sum of array numbers. and another the XOR calculation.
But these solutions still needs to iterate the array.
I wounder if there exists such solution or algorithm to do this without iterating the array.
Also if you are going to flag this question duplicate please refer me the link.
What's the missing number in this array ?
1, 2, 3, 4, 5, 6, *
(The * stands for a number you are not allowed to read, otherwise that would be iterating.)
If there is a missing number in an array you have to inspect it, meaning iterate of the array. No way to do th is without iterating.
In general case, you can't do this. Imagine, that you're given Yves Daoust's sample:
[1, 2, 3, 4, 5, 6, ?]
and you're allowed to read any items of the array, but the last one. What is it? Do I hear seven? No, that's a typical wrong solution:
item = i + (i-1)*(i-2)*(i-3)*(i-4)*(i-5)*(i-6)*F(i)
where F(i) is an arbitrary function (well, not arbitrary, there're some loose restrictions, however - F(i) can't be, say 1/(i-3)). Let
F(i) == 0 -> last item == 7
F(i) == 1 -> last item == 727
F(i) == (pi-i)/720 -> last item == pi
...
You have to have more restrictions, e.g. array represents values of a polynom of the least possible power; in that case the solution is 7

Finding the k'th element in unsorted array using external function

I need to design an algorithm that finds the k'th smallest element in unsorted array using function that called "MED3":
This function finds the n/3 (floor) and 2n/3 (ceil) elements of the array if it was sorted (very similar to median, but instead of n/2 it returns those values).
I thought about kind of partition around those 2 values, and than to continue like QuickSelect, but the problem is that "MED3" doesn't return indices of the 2 values, only the values.
for example, if the array is: 1, 2, 10, 1, 7, 6, 3, 4, 4 it returns 2 (n/3 value) and 4 (2n/3 value).
I also thought to run over the array and to take all the values between 2 and 4 (for example, in the given array above) to new array and then use "MED3" again, but can be duplicates (if the array is 2, 2, 2, 2, ..., 2 I would take all the elements each time).
Any ideas? I must use "MED3".
* MED3 is like a black box, it runs in linear time.
Thank you.
I think you're on the right track, but instead of taking 2 to 4, I'd suggest removing the first n/3 values that are <= MED3.floor() and the first n/3 values that are >= MED3.ceil(). That avoids issues with too many duplicates. If two passes/cycle aren't too expensive, you can remove all values < MED3.floor() + up to a total of n/3 values = MED3.floor() (do the same for ceil())
then repeat until you are at the k'th smallest target.

How to create unique random integer?

is there any posible way to create unique random integer. I know how to make Array or List with unique numbers, but fact is that I need each time different number cause I am creating something like exam, and I am taking values with different id-s from db . If I take duplicate number I would make two same questions on exam.
If there is no way to do it, what do you suggest? how to handle problem. I need to generate different id each time from db.
Thanks in advance.
To create a set of N unique random number from set S, first create a boolean array of size |S| (cardinality of a set, how many elements it contains). Now, each time you generate a random number, check the array to make sure the index of that number is false and set it to true, else pick a different number. Each random number that you generate that had a false index can be added to another array of final numbers.
If I can restate your question, I think you're looking for "How do I randomly pick n numbers out of m available numbers?"
In other words, if you have [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] as available numbers and you want to pick 4, one possible result would be [8, 3, 5, 1].
There are a few ways to do this. One of the simplest is to put the initial list in random order and then take the top n elements.
For example, in Python:
>>> available = range(10)
>>> random.shuffle(available)
>>> available[:4]
[5, 8, 6, 9]
This algorithm is a little bit wasteful, because it randomizes the entire list when you don't really need to.
An improvement would be to, one element at a time, swap that element with a random position after it. This is known as the Fisher-Yates shuffle. It looks like this in pseudo-code:
-- To shuffle an array a of n elements (indices 0..n-1):
for i from 0 to nāˆ’2 do
j ā† random integer such that i ā‰¤ j < n
exchange a[i] and a[j]
But instead of going through the whole list, only go n deep, where n is the number of items you need to pick.
Here's a Python implementation:
import random
def sample(items, how_many):
# copy the list
items = list(items)
# just do the first how_many items
for i in range(how_many):
random_index = random.randrange(i, len(items))
# swap the items
items[i], items[random_index] = items[random_index], items[i]
# return the first how_many
return items[:how_many]
print(sample(range(10), 4))
Many languages/libraries have a built-in way to do this. For example, in Python, the built-in random.sample is the equivalent of my code above:
>>> random.sample(range(10), 4)
[1, 5, 0, 4]
In SQL you can try something like this. The code below will give you 14 unique numbers from 0 to 13 and store it in a temp table and you can do whatever you what with it. tested in sql-server 2008
declare #randNumbers table (number int)
declare #rand int, #howMany int
set #howMany = 14
set #rand = Ceiling(ABS(CHECKSUM(NewId())) % 25)
while ((select COUNT(*) from #randNumbers)<10)
begin
set #rand = Ceiling(ABS(CHECKSUM(NewId())) % #howMany)
BEGIN
IF NOT EXISTS (select * from #randNumbers
WHERE number = #rand)
BEGIN
insert into #randNumbers
values(#rand)
END
END
end
select * from #randNumbers
You can create a sequence in your db and use it to get unique non duplicate auto incremented number... And if you need random number, say something between 0000 and 9999 (just an example), then
1. Create a method that generates this random number
2. Use an array to store this used number or store in db
3. Next time you generate another number, check if you have already used the number.
4. Add this whole code in a while loop so that until you don't get a number which is free it will keep generating new ones to check.

Algorithm to find "most common elements" in different arrays

I have for example 5 arrays with some inserted elements (numbers):
1,4,8,10
1,2,3,4,11,15
2,4,20,21
2,30
I need to find most common elements in those arrays and every element should go all the way till the end (see example below). In this example that would be the bold combination (or the same one but with "30" on the end, it's the "same") because it contains the smallest number of different elements (only two, 4 and 2/30).
This combination (see below) isn't good because if I have for ex. "4" it must "go" till it ends (next array mustn't contain "4" at all). So combination must go all the way till the end.
1,4,8,10
1,2,3,4,11,15
2,4,20,21
2,30
EDIT2: OR
1,4,8,10
1,2,3,4,11,15
2,4,20,21
2,30
OR anything else is NOT good.
Is there some algorithm to speed this thing up (if I have thousands of arrays with hundreds of elements in each one)?
To make it clear - solution must contain lowest number of different elements and the groups (of the same numbers) must be grouped from first - larger ones to the last - smallest ones. So in upper example 4,4,4,2 is better then 4,2,2,2 because in first example group of 4's is larger than group of 2's.
EDIT: To be more specific. Solution must contain the smallest number of different elements and those elements must be grouped from first to last. So if I have three arrrays like
1,2,3
1,4,5
4,5,6
Solution is 1,1,4 or 1,1,5 or 1,1,6 NOT 2,5,5 because 1's have larger group (two of them) than 2's (only one).
Thanks.
EDIT3: I can't be more specific :(
EDIT4: #spintheblack 1,1,1,2,4 is the correct solution because number used first time (let's say at position 1) can't be used later (except it's in the SAME group of 1's). I would say that grouping has the "priority"? Also, I didn't mention it (sorry about that) but the numbers in arrays are NOT sorted in any way, I typed it that way in this post because it was easier for me to follow.
Here is the approach you want to take, if arrays is an array that contains each individual array.
Starting at i = 0
current = arrays[i]
Loop i from i+1 to len(arrays)-1
new = current & arrays[i] (set intersection, finds common elements)
If there are any elements in new, do step 6, otherwise skip to 7
current = new, return to step 3 (continue loop)
print or yield an element from current, current = arrays[i], return to step 3 (continue loop)
Here is a Python implementation:
def mce(arrays):
count = 1
current = set(arrays[0])
for i in range(1, len(arrays)):
new = current & set(arrays[i])
if new:
count += 1
current = new
else:
print " ".join([str(current.pop())] * count),
count = 1
current = set(arrays[i])
print " ".join([str(current.pop())] * count)
>>> mce([[1, 4, 8, 10], [1, 2, 3, 4, 11, 15], [2, 4, 20, 21], [2, 30]])
4 4 4 2
If all are number lists, and are all sorted, then,
Convert to array of bitmaps.
Keep 'AND'ing the bitmaps till you hit zero. The position of the 1 in the previous value indicates the first element.
Restart step 2 from the next element
This has now turned into a graphing problem with a twist.
The problem is a directed acyclic graph of connections between stops, and the goal is to minimize the number of lines switches when riding on a train/tram.
ie. this list of sets:
1,4,8,10 <-- stop A
1,2,3,4,11,15 <-- stop B
2,4,20,21 <-- stop C
2,30 <-- stop D, destination
He needs to pick lines that are available at his exit stop, and his arrival stop, so for instance, he can't pick 10 from stop A, because 10 does not go to stop B.
So, this is the set of available lines and the stops they stop on:
A B C D
line 1 -----X-----X-----------------
line 2 -----------X-----X-----X-----
line 3 -----------X-----------------
line 4 -----X-----X-----X-----------
line 8 -----X-----------------------
line 10 -----X-----------------------
line 11 -----------X-----------------
line 15 -----------X-----------------
line 20 -----------------X-----------
line 21 -----------------X-----------
line 30 -----------------------X-----
If we consider that a line under consideration must go between at least 2 consecutive stops, let me highlight the possible choices of lines with equal signs:
A B C D
line 1 -----X=====X-----------------
line 2 -----------X=====X=====X-----
line 3 -----------X-----------------
line 4 -----X=====X=====X-----------
line 8 -----X-----------------------
line 10 -----X-----------------------
line 11 -----------X-----------------
line 15 -----------X-----------------
line 20 -----------------X-----------
line 21 -----------------X-----------
line 30 -----------------------X-----
He then needs to pick a way that transports him from A to D, with the minimal number of line switches.
Since he explained that he wants the longest rides first, the following sequence seems the best solution:
take line 4 from stop A to stop C, then switch to line 2 from C to D
Code example:
stops = [
[1, 4, 8, 10],
[1,2,3,4,11,15],
[2,4,20,21],
[2,30],
]
def calculate_possible_exit_lines(stops):
"""
only return lines that are available at both exit
and arrival stops, discard the rest.
"""
result = []
for index in range(0, len(stops) - 1):
lines = []
for value in stops[index]:
if value in stops[index + 1]:
lines.append(value)
result.append(lines)
return result
def all_combinations(lines):
"""
produce all combinations which travel from one end
of the journey to the other, across available lines.
"""
if not lines:
yield []
else:
for line in lines[0]:
for rest_combination in all_combinations(lines[1:]):
yield [line] + rest_combination
def reduce(combination):
"""
reduce a combination by returning the number of
times each value appear consecutively, ie.
[1,1,4,4,3] would return [2,2,1] since
the 1's appear twice, the 4's appear twice, and
the 3 only appear once.
"""
result = []
while combination:
count = 1
value = combination[0]
combination = combination[1:]
while combination and combination[0] == value:
combination = combination[1:]
count += 1
result.append(count)
return tuple(result)
def calculate_best_choice(lines):
"""
find the best choice by reducing each available
combination down to the number of stops you can
sit on a single line before having to switch,
and then picking the one that has the most stops
first, and then so on.
"""
available = []
for combination in all_combinations(lines):
count_stops = reduce(combination)
available.append((count_stops, combination))
available = [k for k in reversed(sorted(available))]
return available[0][1]
possible_lines = calculate_possible_exit_lines(stops)
print("possible lines: %s" % (str(possible_lines), ))
best_choice = calculate_best_choice(possible_lines)
print("best choice: %s" % (str(best_choice), ))
This code prints:
possible lines: [[1, 4], [2, 4], [2]]
best choice: [4, 4, 2]
Since, as I said, I list lines between stops, and the above solution can either count as lines you have to exit from each stop or lines you have to arrive on into the next stop.
So the route is:
Hop onto line 4 at stop A and ride on that to stop B, then to stop C
Hop onto line 2 at stop C and ride on that to stop D
There are probably edge-cases here that the above code doesn't work for.
However, I'm not bothering more with this question. The OP has demonstrated a complete incapability in communicating his question in a clear and concise manner, and I fear that any corrections to the above text and/or code to accommodate the latest comments will only provoke more comments, which leads to yet another version of the question, and so on ad infinitum. The OP has gone to extraordinary lengths to avoid answering direct questions or to explain the problem.
I am assuming that "distinct elements" do not have to actually be distinct, they can repeat in the final solution. That is if presented with [1], [2], [1] that the obvious answer [1, 2, 1] is allowed. But we'd count this as having 3 distinct elements.
If so, then here is a Python solution:
def find_best_run (first_array, *argv):
# initialize data structures.
this_array_best_run = {}
for x in first_array:
this_array_best_run[x] = (1, (1,), (x,))
for this_array in argv:
# find the best runs ending at each value in this_array
last_array_best_run = this_array_best_run
this_array_best_run = {}
for x in this_array:
for (y, pattern) in last_array_best_run.iteritems():
(distinct_count, lengths, elements) = pattern
if x == y:
lengths = tuple(lengths[:-1] + (lengths[-1] + 1,))
else :
distinct_count += 1
lengths = tuple(lengths + (1,))
elements = tuple(elements + (x,))
if x not in this_array_best_run:
this_array_best_run[x] = (distinct_count, lengths, elements)
else:
(prev_count, prev_lengths, prev_elements) = this_array_best_run[x]
if distinct_count < prev_count or prev_lengths < lengths:
this_array_best_run[x] = (distinct_count, lengths, elements)
# find the best overall run
best_count = len(argv) + 10 # Needs to be bigger than any possible answer.
for (distinct_count, lengths, elements) in this_array_best_run.itervalues():
if distinct_count < best_count:
best_count = distinct_count
best_lengths = lengths
best_elements = elements
elif distinct_count == best_count and best_lengths < lengths:
best_count = distinct_count
best_lengths = lengths
best_elements = elements
# convert it into a more normal representation.
answer = []
for (length, element) in zip(best_lengths, elements):
answer.extend([element] * length)
return answer
# example
print find_best_run(
[1,4,8,10],
[1,2,3,4,11,15],
[2,4,20,21],
[2,30]) # prints [4, 4, 4, 30]
Here is an explanation. The ...this_run dictionaries have keys which are elements in the current array, and they have values which are tuples (distinct_count, lengths, elements). We are trying to minimize distinct_count, then maximize lengths (lengths is a tuple, so this will prefer the element with the largest value in the first spot) and are tracking elements for the end. At each step I construct all possible runs which are a combination of a run up to the previous array with this element next in sequence, and find which ones are best to the current. When I get to the end I pick the best possible overall run, then turn it into a conventional representation and return it.
If you have N arrays of length M, this should take O(N*M*M) time to run.
I'm going to take a crack here based on the comments, please feel free to comment further to clarify.
We have N arrays and we are trying to find the 'most common' value over all arrays when one value is picked from each array. There are several constraints 1) We want the smallest number of distinct values 2) The most common is the maximal grouping of similar letters (changing from above for clarity). Thus, 4 t's and 1 p beats 3 x's 2 y's
I don't think either problem can be solved greedily - here's a counterexample [[1,4],[1,2],[1,2],[2],[3,4]] - a greedy algorithm would pick [1,1,1,2,4] (3 distinct numbers) [4,2,2,2,4] (two distinct numbers)
This looks like a bipartite matching problem, but I'm still coming up with the formulation..
EDIT : ignore; This is a different problem, but if anyone can figure it out, I'd be really interested
EDIT 2 : For anyone that's interested, the problem that I misinterpreted can be formulated as an instance of the Hitting Set problem, see http://en.wikipedia.org/wiki/Vertex_cover#Hitting_set_and_set_cover. Basically the left hand side of the bipartite graph would be the arrays and the right hand side would be the numbers, edges would be drawn between arrays that contain each number. Unfortunately, this is NP complete, but the greedy solutions described above are essentially the best approximation.

Resources