CountMultiplicativePairs in Python using optimized way - algorithm

The complete problem is given below for which I wrote a Python code and wanted to know the complexity of it or whether it can be optimised more. The solutions are available in C# but the logics are quiet complex.
http://www.whatsjs.com/2018/01/codility-countmultiplicativepairs.html
Here is the solution to the problem:
How to find pairs with product greater than sum
Below the code I wrote in Python. Is there any other way or someone who has tried this problem in python as the C# code explained above doesn't have proper explanation
def solution(A,B):
"""
Count the number of pairs (x, y) such that x * y >= x + y.
"""
M = 1000*1000
max_count=1000*1000*1000
zero=count=0
if len(A)<=1:
return "Length of array A should be greater than 1"
if len(B)<=1:
return "Length of array B should be greater than 1"
if len(A)!=len(B):
return "Length of both arrays should be equal"
C=[0]*len(A)
for (i, elem) in enumerate(A):
C[i]=float(A[i])+float(B[i]/M)
for (i, elem) in enumerate(C):
if elem==0:
zero+=1
if elem>0 and elem<=1:
pass
if elem>1:
for j in range(i+1,len(C)):
if round(C[i]*C[j],2)>=C[i]+C[j]:
count+=1
zero_pairs=int(zero*(zero-1)/2)
count+=zero_pairs
return min(count,max_count)
#return C
#print(solution([0,1,2,2,3,5], [500000, 500000, 0, 0, 0, 20000]))
print(solution([1, 1, 1, 2, 2, 3, 5, 6],[200000, 250000, 500000, 0, 0, 0, 0, 0]))
# print(solution([0, 0, 2, 2], [0, 0, 0, 0]))
# print(solution([1, 3], [500000, 10000]))
# print(solution([1, 3], [400000, 500000]))
#print(solution([0, 0, 0, 0] , [0, 0, 0, 0]))
#print(solution([0, 0, 0, 0] , [1, 1, 1, 1]))
I wanted a more optimised way to solve this, as I feel the complexity currently is O(n^2)

Related

How to check if every element in a 2D Array are connected together

Question is in the title. I have a 2D array:
array = [
[0, 0, 1, 0, 1],
[0, 0, 1, 0, 1],
[1, 1, 1, 1, 1],
[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0]
]
How do I check to see if every element "1" in this example are all connected together as neighbors either laterally or horizontally. In this example the function should return TRUE since all of the 1's are all connected together. In contrast:
array = [
[0, 0, 0, 1, 1],
[0, 0, 0, 1, 1],
[0, 0, 0, 0, 0],
[1, 1, 0, 0, 0],
[1, 1, 0, 0, 0]
]
This should return FALSE, since their is a divide between the 1's and not all of them are neighbors.
My initial thought was to iterate through the array and check to see if any of the adjacent items were 1's or not. However, this doesn't work since two elements can be next to each other yet away from the rest of the group. Any help is greatly appreciated.
You can use BFS or DFS for that.
These are exploration algorithms that helps you to discover all nodes connected to your starting one.
The "trick" is to think of your matrix as a graph where:
V = { (i,j) | a[i][j] == 1} (informally, all locations where there is 1 in the matrix
E = { ((i1, j1), (i2, j2)) | (i1, j1), (i2, j2) are adjacent }
Then, just find a place where a[i][j] == 1, and start a BFS or DFS from it to disccover all reachable nodes.
Once you are done, iterate the matrix again, and see if each a[i][j] == 1 element was discovered.
Good luck!
The correct answer for this question is counting all the elements that are 1's then finding any element that is a '1' then using a flood fill algorithm that counts the amount of 1's. If the two values are equal then the answer is True if not then false.
https://en.wikipedia.org/wiki/Flood_fill

Given an array of n numbers find all the ways of inserting "+" and "-" between them so that the result of the expression is positive

Given an array of n numbers find all the ways of inserting + and - between them so that the result of the expression is positive.
I've found this problem recently and I thought it was interesting, but I'm not exactly sure how to solve it. I think I should try backtracking, no?
Any help or hints are deeply appreciated!
Edit: Would this be a correct solution? (I wrote it in python)
def outputSolution(list):
print(list)
def solution(x, dim):
return len(x) == dim-1
def consistent(list, array):
partial_sum = array[0]
for i in range(len(list)):
if list[i] == 0:
partial_sum = partial_sum - array[i+1]
if list[i] == 1:
partial_sum = partial_sum + array[i+1]
absolute_remaining_sum = 0
for i in range(len(list)+1, len(array)): #the remaining elements in array
absolute_remaining_sum =absolute_remaining_sum + abs(array[i])
if partial_sum + absolute_remaining_sum < 0:
return False
else:
return True
def solve(list, array):
"""
array - the array of n given integers
list - the candidate to a solution
"""
dim = len(array)
for el in range(2): # el = 0 or 1 (0 for - and 1 for +)
if len(list) < dim - 1:
list.append(el)
if consistent(list, array):
if solution(list, dim):
outputSolution(list)
solve(list[:], array)
list.pop()
solve([], array)
My thought process was that there are n-1 gaps between those numbers. Each gap can have a '+' or a '-' in it. And so I build a list where list[i] is equal with 0 if between array[i] and array[i+1] there is an "-", and list[i] is equal with 0 if between array[i] and array[i+1] there is an "+". And I generated all the possible ways of choosing the values in the list, then I checked if that possible candidate is consistent or not. And I said that if the partial sum (calculated using the + and - in our current list) added to the maximum sum of the remaining elements of the given array is a negative number, then the candidate is inconsistent. If the candidate is consistent and it has the required length, then I said that it is a solution.
For example, if I had the array "array = [1,2,3,4,5,6,7]" as input, I was given the following solutions:
[0, 0, 0, 1, 1, 1]
[0, 0, 1, 0, 1, 1]
[0, 0, 1, 1, 0, 1]
[0, 0, 1, 1, 1, 0]
[0, 0, 1, 1, 1, 1]
[0, 1, 0, 0, 1, 1]
[0, 1, 0, 1, 0, 1]
[0, 1, 0, 1, 1, 0]
[0, 1, 0, 1, 1, 1]
[0, 1, 1, 0, 0, 1]
[0, 1, 1, 0, 1, 0]
[0, 1, 1, 0, 1, 1]
[0, 1, 1, 1, 0, 1]
[0, 1, 1, 1, 1, 0]
[0, 1, 1, 1, 1, 1]
[1, 0, 0, 0, 1, 1]
[1, 0, 0, 1, 0, 1]
[1, 0, 0, 1, 1, 0]
[1, 0, 0, 1, 1, 1]
[1, 0, 1, 0, 0, 1]
[1, 0, 1, 0, 1, 1]
[1, 0, 1, 1, 0, 1]
[1, 0, 1, 1, 1, 0]
[1, 0, 1, 1, 1, 1]
[1, 1, 0, 0, 1, 1]
[1, 1, 0, 1, 0, 1]
[1, 1, 0, 1, 1, 0]
[1, 1, 0, 1, 1, 1]
[1, 1, 1, 0, 0, 1]
[1, 1, 1, 0, 1, 0]
[1, 1, 1, 0, 1, 1]
[1, 1, 1, 1, 0, 0]
[1, 1, 1, 1, 0, 1]
[1, 1, 1, 1, 1, 0]
[1, 1, 1, 1, 1, 1]
Backtracking is indeed a reasonable strategy. Since you need to enumerate, there's only one pruning trick that makes an asymptotic difference. Suppose that the array starts with a very large negative number, e.g.,
−50 10 10 10 10 1 2 3 4 5
The sum always includes a −50 term, so the sign for each 10 must be positive since otherwise the remaining numbers aren't large enough to make the overall sum positive. By making the example bigger (more and bigger numbers), we can create an exponential gap between the complexity of naive backtracking and the number of solutions.
If we implement the usual depth-first backtracking strategy and maintain the sum of the absolute values of the remaining array elements, then we can prune every node where the partial sum plus the sum of absolute values is not positive. Since every node not pruned yields at least one solution, we end up with an optimal output-sensitive time complexity.

How can I generate all the possible binary lists with 4 elements? (Using Python)

I'm wondering is there a way I can generate a list whose sublists are all the possible combinations of 0 and 1 with 4 elements? To generate an individual sublist, I have
import random
binum = np.random.randint(2, size=4).tolist()
But how can I get a full list to contain all the possible sublists, each appears once? So the desired output looks like:
[[0,0,0,0],[0,0,0,1]...[1,0,0,1]...[1,1,1,1]]
(There're 16 elements in total for this case). Thanks:)
You basically want all the binary numbers from 0 to 2**n. So, you could use the following code to generate them:
def get_binary_numbers(n):
format_str = f"0{n}b"
max_binary_number = 2**n
return [list(format(i, format_str)) for i in range(max_binary_number)]
Be careful with its performance, if you need big values of n then maybe use the same idea with other ways to generate the lists.
Just iterate over numbers from 0 to 15 and get their binary representation:
In [1]: N = 4
In [2]: def getbit(value, bitnum):
...: mask = 1 << bitnum
...: return (value & mask) >> bitnum
...:
In [3]: [[getbit(i, j) for j in range(N)] for i in range(2 ** N)]
Out[3]:
[[0, 0, 0, 0],
[1, 0, 0, 0],
[0, 1, 0, 0],
[1, 1, 0, 0],
[0, 0, 1, 0],
[1, 0, 1, 0],
[0, 1, 1, 0],
[1, 1, 1, 0],
[0, 0, 0, 1],
[1, 0, 0, 1],
[0, 1, 0, 1],
[1, 1, 0, 1],
[0, 0, 1, 1],
[1, 0, 1, 1],
[0, 1, 1, 1],
[1, 1, 1, 1]]
Same in numpy:
np.unpackbits(np.expand_dims(np.arange(2 ** N, dtype=np.uint8), -1),
axis=1, bitorder='little', count=N)
Also, you can generate the next element by implementing increment operation on binary array:
def gen(l=4):
a = [0 for _ in range(l)]
while True:
yield a.copy()
i = 0
while i < l and a[i]:
a[i] = 0
i += 1
if i == l:
break
a[i] = 1
You can get the cartesian product of your input using itertools library
from itertools import product
bit_sets = product(range(2), repeat=4)
# format your data code ...
more details at https://docs.python.org/3.1/library/itertools.html?highlight=combinations#itertools.product
This just needs the actual necessary parameters from #yesk13's answer, really, using the cartesian product from itertools
for p in it.product([0, 1], repeat=4):
print(p)
(0, 0, 0, 0)
(0, 0, 0, 1)
(0, 0, 1, 0)
(0, 0, 1, 1)
(0, 1, 0, 0)
(0, 1, 0, 1)
(0, 1, 1, 0)
(0, 1, 1, 1)
(1, 0, 0, 0)
(1, 0, 0, 1)
(1, 0, 1, 0)
(1, 0, 1, 1)
(1, 1, 0, 0)
(1, 1, 0, 1)
(1, 1, 1, 0)
(1, 1, 1, 1)

Why does my diagonal matrix calculator not return the total?

My method should take an array of subarrays, find the sum of the first value of the first array, the second value of the second array, the third value of the third array, and so on. Some examples of inputs and expected results are as follows:
exampleArray = [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
]
diagonalSum(exampleArray) # => 4
exampleArray = [
[1, 0, 0, 0, 0],
[0, 1, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 1, 0],
[0, 0, 0, 0, 1]
]
diagonalSum(exampleArray) # => 5
I wrote this:
def diagonalSum(matrix)
total = 0
counter = 0
while matrix.length <= counter + 1 do
total += matrix[counter][counter]
counter += 1
end
total
end
and it returns 0.
It's easiest to convert the array to a matrix and apply Matrix#trace.
require 'matrix'
arr = [[1, 0, 0, 7],
[0, 2, 0, 0],
[0, 0, 3, 0],
[8, 0, 0, 4]]
Matrix[*arr].trace
#=> 10
According to the code you provide, in which the input is an array of arrays, the first advice I could give you is that in Ruby you must avoid using for/while loops and make use of iterators such as each/each_with_index instead (based on this Ruby style guide and the suggestions of #tadman and #Yu Hao).
The each with index iterator takes a Ruby block with the current array of the iteration along with its index position, so you don't need to define your own index variable and update it in every iteration.
Applying this to your code will result in the following:
def diagonal_sum(matrix)
total = 0
matrix.each_with_index do |row, index|
total+=row[index]
end
total
end
Also note that the convention in Ruby is to write variable and method names in snake_case (according to the previous style guide).

Ruby - Graph adjacency matrix into variable

I am trying to edit an algorithm found here.
I want the adjacency matrix to be loaded from file (formatting of the file doesn't matter to me, it can be either like this [0,1,1,0] or just 0110) with G = file.read().split("\n")
However, I get an error no implicit conversion of Fixnum into String (TypeError)
And I already know I need to convert this string to ints, but how to do it properly to not lose the formatting required by this DFS method?
I guess it's pretty easy, but I'm a begginer in Ruby (and graphs :v) and can't get it to work...
Edit:
So the code I'm using to read from file to an array of arrays is:
def read_array(file_path)
File.foreach(file_path).with_object([]) do |line, result|
result << line.split.map(&:to_i)
end
end
And the result I get from a file (for example)
01101010
01010101
01010110
10101011
01011111
is this:
=> [[[1101010], [1010101], [1010110], [10101011], [1011111]]]
What I need, however, is:
=> [[[1,1,0,1,0,1,0], [1,0,1,0,1,0,1], [1,0,1,0,1,1,0], [1,0,1,0,1,0,1,1], [1,0,1,1,1,1,1]]]
So that it would work with the algorithm mentioned in the first line of my post (I'll copy it here, if it takes too much place I can delete it and leave link only):
G = [0,1,1,0,0,1,1], # A
[1,0,0,0,0,0,0],
[1,0,0,0,0,0,0],
[0,0,0,0,1,1,0],
[0,0,0,1,0,1,1],
[1,0,0,1,1,0,0],
[1,0,0,0,1,0,0] # G
LABLES = %w(A B C D E F G)
def dfs(vertex)
print "#{LABLES[vertex]} " # visited
edge = 0
while edge < G.size
G[vertex][edge] = 0
edge += 1
end
edge = 0
while edge < G.size
if ( G[edge][vertex] != 0 && edge != vertex)
dfs(edge)
end
edge += 1
end
end
dfs(0)
split's default separator is a whitespace. To make it split every char you need to explicitly say it:
'01101101'.split.map(&:to_i)
# => [ 1101101 ]
'01101101'.split('').map(&:to_i)
# => [ 0, 1, 1, 0, 1, 1, 0, 1 ]
you can also use chars to do the same job:
'01101101'.chars.map(&:to_i)
# => [ 0, 1, 1, 0, 1, 1, 0, 1 ]
I don't know how your read_array is used, but it can be simplified to:
def read_array(file_path)
File.foreach(file_path).map do |line|
line.chomp.chars.map(&:to_i)
end
end
read_array('my_file.txt')
# => [[1, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1], [1, 0, 1, 0, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1], [1, 0, 1, 1, 1, 1, 1]]
If you still get the extra [, you can either take only the first item:
my_array[0]
Or (if there is more than one item the uber-array) - use flat_map:
uber_array = [[[1, 0, 1, 0, 1, 0, 1], [1, 0, 1, 0, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1]],
[[1, 0, 1, 0, 1, 0, 1, 1], [1, 0, 1, 1, 1, 1, 1]]]
uber_array.flat_map { |a| a }
# => [[1, 0, 1, 0, 1, 0, 1], [1, 0, 1, 0, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1], [1, 0, 1, 0, 1, 0, 1, 1], [1, 0, 1, 1, 1, 1, 1]]

Resources