Majority tree evaluation - python

Consider a complete ternary tree of depth h, composed of a root attached to three complete ternary trees of depth h - 1. There are n = 3^h leaves and each leaf has a boolean value associated to it. Each internal node, including the root, equals the value of the majority of its children.
Here is an example of a tree of depth 2:
Given the leaves input vector [0, 0, 1, 0, 1, 0, 1, 1, 1], we would like to find the root of the tree. In order to find the root, we could evaluate all the leaves and internal nodes (i.e. 3^h operations). But we may be able to evaluate fewer nodes. In the example above, we can see that the value of the first internal node (most left) can be evaluated after examining its first two children. Similarly at depth = 1, the first two nodes are sufficient to find the root of the tree.
I have been thinking over this problem but I can't find a good way to tackle the problem.
import numpy as np
import random
class node:
def __init__(self):
self.low = None
self.mid = None
self.high = None
def put(self, low, mid, high):
self.low = low
self.mid = mid
self.high = high
return self
class ternary_tree:
def __init__(self, leaves, count= 0):
self.leaves = leaves
self.root = node()
self.value = None
self.count = count
def get_root(self):
self.root.put(self.leaves[0], self.leaves[1], self.leaves[2])
self.value = majority(self.root)
return self.value
def majority(node):
global ops_count
r1, r2, r3 = random.sample([node.low, node.mid, node.high], 3)
if r1 > 0:
ops_count += 1
if r2 > 0:
ops_count += 1
return 1;
elif r3 > 0:
ops_count += 1
return 1;
else:
return 0;
else:
ops_count += 1
if r2 == 0:
ops_count += 1
return 0;
elif r3 == 0:
ops_count += 1
return 0;
else:
return 1;
if __name__ == "__main__":
h = 2 # depth of the tree
my_leaves = [random.randint(0,1) for i in range(0, 3**h)] #input vector
ops_count = 0 #Counting the number of steps
nb_trees = len(my_leaves) // 3
my_trees = [ternary_tree(leaves=my_leaves[i:i+3]) for i in range(0, nb_trees)]
new_leaves = []
t1, t2, t3 = random.sample(my_trees, 3)
new_leaves.append(t1.get_root())
new_leaves.append(t2.get_root())
if new_leaves[0] == new_leaves[1]:
new_leaves.append(new_leaves[0])
else:
new_leaves.append(t3.get_root())
ternary_tree(leaves=new_leaves).get_root()
I think the code does the job, but it is not optimal in the sense that it still checks all the internal node and doesn't skip the redundant nodes. I think the right approach is to have a recursive algorithm like a binary search tree, but I cannot make the link between BST and the majority tree evaluation.
I would appreciate if you could give me any indication on how to solve this problem. Thanks!
The illustration comes from here: http://www.math.ucsd.edu/~gptesler/188/MajTree/majtree.html.

Indeed, recursion would be the way to find the root value. I don't really see a need to create the tree data structure: when the values of all the leaf values are input as a list, then we really have all information in a suitable format.
Here is how the majority function could look when it is given only the list of leaf values:
import random
def majority(leaves):
counter = 0
def recur(start, size):
nonlocal counter
if size == 1:
counter += 1 # about to access a leaf's value
return leaves[start]
size //= 3
# Randomly choose which child we will leave as "plan B" in case
# two others give opposite values
last = random.randint(0, 2)
# Get the values of the two other children, using recursion
val1 = recur(start + ((last+1)%3)*size, size)
val2 = recur(start + ((last+2)%3)*size, size)
# If equal, we do not need to retrieve the remaining child's value
if val1 == val2:
return val1
# Too bad,... we need the value of the third child to break the tie
return recur(start + last*size, size)
rootval = recur(0, len(leaves))
return rootval, counter
You would call it like this:
h = 2
leaves = [random.randint(0,1) for i in range(0, 3**h)]
rootval, counter = majority(leaves)
print("The leaves are {}".format(leaves))
print("Accessed {} leaves to find that root's value is {}".format(counter, rootval))

Related

Efficient implemetation of BFS on python

There is a following problem:
Given set of n points with x, y coordinates. Distance between them is L1 : sum of abs of differences of corresponding coordinates. There is an edge between two points if distance between them is no more than given k. Given different start and end points I need to find the number of edges in minimum path between them, and minimum is counted by number of edges, not overall length. If there is no path between given nodes, I should print -1
It seemed to me that this problem is solved by BFS. But since we have to have list of edges to use BFS, which is O(n^2), I decided to merge edge processing with BFS. So my code looks like that:
n = int(input())
cities = []
def read_two_numbers():
s = input()
s = s.split(' ')
return list(map(int,s))
for i in range(n):
cities.append(read_two_numbers())
def distance(city1, city2):
return abs(cities[city1][0] - cities[city2][0]) + abs(cities[city1][1] - cities[city2][1])
k = int(input())
start, end = read_two_numbers()
start -= 1
end -= 1
def find_minimum_path():
reached = [start]
not_reached = [i for i in range(n) if i != start]
level = 1
while len(not_reached) > 0:
new_reached = []
# print(f'reached is {reached}')
# print(f'not_reached is {not_reached}')
for city_from in reached:
new_not_reached = []
for city_to in not_reached:
if distance(city_from, city_to) <= k:
if city_to == end:
print(level)
return
new_reached.append(city_to)
else:
new_not_reached.append(city_to)
not_reached = new_not_reached
if len(not_reached) == 0:
break
reached = new_reached
level += 1
print(-1)
return
find_minimum_path()
The problem is that this solution exceeds time limit. Is there a way to solve this task more efficiently? Or it is connected with low speed of Python itself?

Subset sum with minimum elements

Given a sorted list of integers, always containing 1. Find a target value using the minimum amount of elements to sum to the target value. All numbers can be used more than one.
e.x. {1,9,10} Target = 18, solution is 2 elements (9 twice).
{1,3,5,7} Target = 15, solution is 3 elements (7,7,1 or 5,5,5)
I understand that we should check whether we use an element up to its maximum amount to fill the target value but I am confused on how to correctly count the number of elements used once we have a correct recursive return.
def main():
sections = list(map(int,input().split(" ")))
t = int(input())
print((subset_sum(sections,len(sections)-1,t)), "elements minimum")
def subset_sum(X, i, t):
count = 0
if t == 0:
return 1
if t < 0 or abs(i) == len(X):
return 0
for z in range(0,t//X[i]):
count += subset_sum(X,i-1,t-(z*X[i]))
return count
if __name__ == "__main__":
main()
Is my base case incorrect? Since I want the minimum should the incorrect case return 1? Or do I have a mistake when I call the recursion?
I think the code is trying to solve a different problem than the one described in the title. You seem to be counting the number of different ways to make change for amount t using denominations in X. Here is a version of your code that does this:
def subset_sum(X, i, t):
count = 0
if t == 0:
return 1
if t < 0 or i < 0:
return 0
for z in range(0,t//X[i] + 1):
count += subset_sum(X,i-1,t-(z*X[i]))
return count
subset_sum([5, 2, 1], 2, 30) # 58 ways to make change
# same as the following Mathematica code:
# Length#IntegerPartitions[30, All, {1, 2, 5}]
In order to find the minimum number of coins needed to achieve amount t, you need to modify your inductive step. Instead of adding the amounts for smaller values of t, you need to take the minimum:
from functools import lru_cache
def coin_change(denominations, target):
'Return minimum number of coins with given denominations to make a target sum'
#lru_cache(None)
def f(target):
if target == 0:
return 0
elif target < 0:
return float("inf")
return min(f(target - d) for d in denominations) + 1
return f(target)
coin_change([1, 2, 5], 30) # minimum number of coins is 6

Eliminating nearest elements from a list or range

So what I have to do is find the smallest number among those that are in the list, and remove from the list both that number and whichever of its current immediate neighbors is larger. This function should repeat until the largest number in the original list gets eliminated and I have to return the number of times it took to achieve this goal. For example, [1,6,4,2,5,3] would remove element 1 and its current largest neighbor element which is 6, thus reaching the goal in 1 step. My code worked for a list but it did not work when I inputted a range in items. So could anybody help?
def elements(items):
max_value = max(items)
count = 0
while max_value in items:
n = len(items)
right_of_items = 0
left_of_items = 0
min_value = min(items)
min_value_index = items.index(min_value)
if (min_value_index -1 >= 0):
left_of_items = items[min_value_index-1]
if (min_value_index+1 < n):
right_of_items = items[min_value_index+1]
if left_of_items > right_of_items:
items.remove(left_of_items)
items.remove(min_value)
count += 1
else:
items.remove(right_of_items)
items.remove(min_value)
count += 1
return count
The problem with range is that it doesn't support access to a specific item.
So that, you either need to build a list from that range, or use more advanced algorithm which will be able to find the solution in a single pass.
As we won't know the smallest and largest items unless we're analysed the whole list, I guess, there's no such algorithm, that could solve in less than 2 passes, so consider just doing
items = list(items)
at the beginning of your function.
Also I would suggest to not to store the values of a smallest and largest items themselves. Better store their positions. So that you'll be able to access (and remove) neighbours much more effeciently. Like:
def find_largest_neighbour(lst, pos):
if pos <= 0:
return pos + 1
if pos + 1 >= len(lst):
return pos - 1
if lst[pos - 1] <= lst[pos + 1]:
return pos - 1
else:
return pos + 1
def find_min_and_max(lst):
min_pos = max_pos = 0
for i, v in enumerate(lst):
if v < lst[min_pos]:
min_pos = i
if v > lst[max_pos]:
max_pos = i
return min_pos, max_pos
def solve(lst):
number_of_iterations = 0
while True:
number_of_iterations += 1
min_pos, max_pos = find_min_and_max(lst)
max_pos_about = find_largest_neighbour(lst, min_pos)
if max_pos_about == max_pos:
# The largest item found about the smallest, we're done
break
else:
# Getting rid of the smallest item and its largest bro
del lst[min_pos]
del lst[max_pos_about]
# as we used index lookups above, we
# won't search through the list twice
return number_of_iterations

Largest subarray with sum equal to 0

This is a typical interview question. Given an array that contains both positive and negative elements without 0, find the largest subarray whose sum equals 0. I tried to solve this. This is what I came up with.
def sub_array_sum(array,k=0):
start_index = -1
hash_sum = {}
current_sum = 0
keys = set()
best_index_hash = {}
for i in array:
start_index += 1
current_sum += i
if current_sum in hash_sum:
hash_sum[current_sum].append(start_index)
keys.add(current_sum)
else:
if current_sum == 0:
best_index_hash[start_index] = [(0,start_index)]
else:
hash_sum[current_sum] = [start_index]
if keys:
for k_1 in keys:
best_start = hash_sum.get(k_1)[0]
best_end_list = hash_sum.get(k_1)[1:]
for best_end in best_end_list:
if abs(best_start-best_end) in best_index_hash:
best_index_hash[abs(best_start-best_end)].append((best_start+1,best_end))
else:
best_index_hash[abs(best_start-best_end)] = [(best_start+1,best_end)]
if best_index_hash:
(bs,be) = best_index_hash[max(best_index_hash.keys(),key=int)].pop()
return array[bs:be+1]
else:
print "No sub array with sum equal to 0"
def Main():
a = [6,-2,8,5,4,-9,8,-2,1,2]
b = [-8,8]
c = [-7,8,-1]
d = [2200,300,-6,6,5,-9]
e = [-9,9,-6,-3]
print sub_array_sum(a)
print sub_array_sum(b)
print sub_array_sum(c)
print sub_array_sum(d)
print sub_array_sum(e)
if __name__ == '__main__':
Main()
I am not sure if this will satisfy all the edge case. if someone can comment on that, it would be excellent Also i want to extend this to sum equalling to any K not just 0. How should i go about it. And any pointers to optimize this further is also helpful.
You have given a nice, linear-time solution (better than the two other answers at this time, which are quadratic-time), based on the idea that whenever sum(i .. j) = 0, it must be that sum(0 .. i-1) = sum(0 .. j) and vice versa. Essentially you compute the prefix sums sum(0 .. i) for all i, building up a hashtable hash_sum in which hash_sum[x] is a list of all positions i having sum(0 .. i) = x. Then you go through this hashtable, one sum at a time, looking for any sum that was made by more than one prefix. Among all such made-more-than-once sums, you choose the one that was made by a pair of prefixes that are furthest apart -- this is the longest.
Since you already noticed the key insight needed to make this algorithm linear-time, I'm a bit puzzled as to why you build up so much unnecessary stuff in best_index_hash in your second loop. For a given sum x, the furthest-apart pair of prefixes that make that sum will always be the smallest and largest entries in hash_sum[x], which will necessarily be the first and last entries (because that's the order they were appended), so there's no need to loop over the elements in between. In fact you don't even need a second loop at all: you can keep a running maximum during your first loop, by treating start_index as the rightmost endpoint.
To handle an arbitrary difference k: Instead of finding the leftmost occurrence of a prefix summing to current_sum, we need to find the leftmost occurrence of a prefix summing to current_sum - k. But that's just first_with_sum{current_sum - k}.
The following code isn't tested, but should work:
def sub_array_sum(array,k=0):
start_index = -1
first_with_sum = {}
first_with_sum{0} = -1
best_start = -1
best_len = 0
current_sum = 0
for i in array:
start_index += 1
current_sum += i
if current_sum - k in first_with_sum:
if start_index - first_with_sum{current_sum - k} > best_len:
best_start = first_with_sum{current_sum - k} + 1
best_len = start_index - first_with_sum{current_sum - k}
else:
first_with_sum{current_sum} = start_index
if best_len > 0:
return array[best_start:best_start+best_len-1]
else:
print "No subarray found"
Setting first_with_sum{0} = -1 at the start means that we don't have to treat a range beginning at index 0 as a special case. Note that this algorithm doesn't improve on the asymptotic time or space complexity of your original one, but it's simpler to implement and will use a small amount less space on any input that contains a zero-sum subarray.
Here's my own answer, just for fun.
The number of subsequences is quadratic, and the time to sum a subsequence is linear, so the most naive solution would be cubic.
This approach is just an exhaustive search over the subsequences, but a little trickery avoids the linear summing factor, so it's only quadratic.
from collections import namedtuple
from itertools import chain
class Element(namedtuple('Element', ('index', 'value'))):
"""
An element in the input sequence. ``index`` is the position
of the element, and ``value`` is the element itself.
"""
pass
class Node(namedtuple('Node', ('a', 'b', 'sum'))):
"""
A node in the search graph, which looks like this:
0 1 2 3
\ / \ / \ /
0-1 1-2 2-3
\ / \ /
0-2 1-3
\ /
0-3
``a`` is the start Element, ``b`` is the end Element, and
``sum`` is the sum of elements ``a`` through ``b``.
"""
#classmethod
def from_element(cls, e):
"""Construct a Node from a single Element."""
return Node(a=e, b=e, sum=e.value)
def __add__(self, other):
"""The combining operation depicted by the graph above."""
assert self.a.index == other.a.index - 1
assert self.b.index == other.b.index - 1
return Node(a=self.a, b=other.b, sum=(self.sum + other.b.value))
def __len__(self):
"""The number of elements represented by this node."""
return self.b.index - self.a.index + 1
def get_longest_k_sum_subsequence(ints, k):
"""The longest subsequence of ``ints`` that sums to ``k``."""
n = get_longest_node(n for n in generate_nodes(ints) if n.sum == k)
if n:
return ints[n.a.index:(n.b.index + 1)]
if k == 0:
return []
def get_longest_zero_sum_subsequence(ints):
"""The longest subsequence of ``ints`` that sums to zero."""
return get_longest_k_sum_subsequence(ints, k=0)
def generate_nodes(ints):
"""Generates all Nodes in the graph."""
nodes = [Node.from_element(Element(i, v)) for i, v in enumerate(ints)]
while len(nodes) > 0:
for n in nodes:
yield n
nodes = [x + y for x, y in zip(nodes, nodes[1:])]
def get_longest_node(nodes):
"""The longest Node in ``nodes``, or None if there are no Nodes."""
return max(chain([()], nodes), key=len) or None
if __name__ == '__main__':
def f(*ints):
return get_longest_zero_sum_subsequence(list(ints))
assert f() == []
assert f(1) == []
assert f(0) == [0]
assert f(0, 0) == [0, 0]
assert f(-1, 1) == [-1, 1]
assert f(-1, 2, 1) == []
assert f(1, -1, 1, -1) == [1, -1, 1, -1]
assert f(1, -1, 8) == [1, -1]
assert f(0, 1, -1, 8) == [0, 1, -1]
assert f(5, 6, -2, 1, 1, 7, -2, 2, 8) == [-2, 1, 1]
assert f(5, 6, -2, 2, 7, -2, 1, 1, 8) == [-2, 1, 1]
I agree with sundar nataraj when he says that this must be posted to the code review forum.
For fun though I looked at your code. Though I am able to understand your approach, I fail to understand the need to use Counter.
best_index_hash[start_index] = [(0,start_index)] - Here best_index_hash is of the type Counter. Why are you assigning a list to it?
for key_1, value_1 in best_index_hash.most_common(1) - You trying to get largest subsequence and for that you are using most_common as the answer. This is not intuitive semantically.
I am tempted to post a solution but I will wait for you to edit the code snippet and improve it.
Addendum
For fun, I had a go at this puzzle and I present my effort below. I make no guarantees of correctness/completeness.
from collections import defaultdict
def max_sub_array_sum(a, s):
if a:
span = defaultdict(lambda : (0,0))
current_total = 0
for i in xrange(len(a)):
current_total = a[i]
for j in xrange (i + 1, len(a)):
current_total += a[j]
x,y = span[current_total]
if j - i > y - x:
span[current_total] = i,j
if s in span:
i, j = span[s]
print "sum=%d,span_length=%d,indices=(%d,%d),sequence=%s" %\
(s, j-i + 1, i, j, str(a[i:j + 1]))
return
print "Could not find a subsequence of sum %d in sequence %s" % \
(s, str(a))
max_sub_array_sum(range(-6, -1), 0)
max_sub_array_sum(None, 0)
max_sub_array_sum([], 0)
max_sub_array_sum(range(6), 15)
max_sub_array_sum(range(6), 14)
max_sub_array_sum(range(6), 13)
max_sub_array_sum(range(6), 0)
Here's the solution taken from LeetCode :
def sub_array_sum(nums, k=0):
count, sum = 0, 0
map = dict()
map[0] = 1
for i in range(len(nums)):
sum += nums[i]
if map.__contains__(sum - k):
count += map[sum - k]
map[sum] = map.get(sum, 0) + 1
return count

Factoring, Substituting and printing roots of a polynomial

We've been asked to make a program that takes in polynomial coefficients(i.e. [1, 2, 1] representing x^2 + 2x + 1 and use the rational root theorem to find and print the roots in a list of fractional strings. We were only allowed to use import math. The problem with my code is it only works if I input [1, 2, 1] and when I input other polynomials, it prints a blank list.
import math
roots = []
pr = []
pf = []
qf = []
count = 1
t = 0
co = raw_input('Input Coefficients: ')
co = co.split(' ')
p = co[0]
q = co[-1]
def gcd(a,b): #function for computing gcf between two numbers. will be used to simplify fractions into lowest terms
r = a%b
if r == 0:
return b
else:
return gcd(b,r)
def rmvvalues(coefficients, val): #remove the zeroes from the list because zero is not factorable
for i in range(coefficients.count(val)):
coefficients.remove(val)
while int(p) == 0: #if the constant was 0, it means 0 is automatically a root
roots.append(0)
rmvvalues(co, str('0'))
p = co[0]
q = co[-1]
while count <= math.fabs(int(p)): #factors of the constant
if int(p) % count == 0:
pf.append(count)
pf.append(count*(-1))
count = count + 1
else:
count = count + 1
count = 1
while count <= math.fabs(int(q)): #factors of the last term
if int(q) % count == 0:
qf.append(count)
qf.append(count*(-1))
count = count + 1
else:
count = count + 1
count = 1
for i in range(len(pf)): #every element in the first list of factors is to be divided by every element of the other list of factors
for j in range(len(qf)):
result = 0
for c in range(len(co)): #and each pf/qf is to be checked if it would make the polynomial zero
result = int(result) * (int(pf[i])/int(qf[j])) + int(co[c])
if result == 0: #if it makes it zero, it appends the answer in fraction string to the list of roots
if (int(pf[i]) / int(qf[j])) == 1: #add 1 to the list of roots if p/q == 1 and would make the equation zero
roots.append(1)
elif int(pf[i])/int(qf[j]) == -1: #add -1 to the list of roots if p/q == -1 and would make the equation zero
roots.append(-1)
else: #if they would be fractions
a = int(pf[i]) / int(gcd(int(pf[i]),int(qf[j])))
b = int(qf[j]) / int(gcd(int(pf[i]),int(qf[j])))
roots.append(str(a) + '/' +str(b))
roots = sorted(set(roots))
print roots
p.s.(I just copy/pasted the code from my editor so the indentation may be a little off)
I have an implementation to propose, but not a debugging of yours.
The fact is that you are writing a big one bloc code that I don't understand.
As I want to help you I give you an example of implementation that I hope you will find helpful AND readable. My big suggest when coding is to split the code in little function easy to understand with meaningful name, and then add an algorithm function that call all the sub-functions. This make the algorithm to looks like pseudocode and be mode understandable.
Additionally, more specifically related to python, your code is supposed to follow some rule. Runtime code is supposed to be placed after if __name__ == "__main__": bloc.
Hope it helps you (take a look at the findSolution method):
class Polynom():
""" A class representing a polynom that provides methods to find roots of the polynom. """
#-----------------------------------------------------------------------------------------
def __init__(self, coefficients):
"""
Constructor of the class.
Takes a coeficient list [n, n-1, ..., n1, n0] that are the coefficients of a polynom.
Example :
Polynom([1,2,3]) stand for : x² + 2x + 3
"""
self.coefficients = coefficients
#-----------------------------------------------------------------------------------------
def _dividers(self, coefficient):
"""Returns the list of the divider of a number by filtering the values in [1, 2, ... , |coefficient| ] that divide coefficient. """
def filter_dividers(coefficient, candidate_values):
# lambda function explanations : http://www.diveintopython.net/power_of_introspection/lambda_functions.html
return filter(lambda number : coefficient%number == 0, candidate_values)
return list(filter_dividers(coefficient, range(1, abs(coefficient)+1)))
#-----------------------------------------------------------------------------------------
def _gcd(self, x, y):
""" Returns the greatest common diviser of the integers x and y """
if y == 0:
return abs(x)
else:
r = x%y
return self._gcd(y, r)
#-----------------------------------------------------------------------------------------
def _positiveAndNegativeCombinations(self, p_values, q_values):
"""
Returns the list of positives and negatives combination of (p,q) pairs.
Example :
[1,2]
-> [(1,4), (-1,4), (2,4), (-2,4), (1,5), (-1,5), (2,5), (-2,5)]
[4,5]
"""
def combinations(p, q_values):
if len(q_values) == 1:
return [(p, q_values[0])]
else:
return [(p, q_values[0])] + combinations(p, q_values[1:])
result = []
for p in p_values:
p_combinations = combinations(p, q_values)
for combination in p_combinations:
result += [combination, (-1*combination[0], combination[1])]
return result
#-----------------------------------------------------------------------------------------
def __repr__(self):
""" Representation of the object in a string for printing purpose."""
def from_number_to_string_exposant(number):
"""
Returns a string that is the number given as exposant.
Example : 1 -> "¹"
"""
utf8_exposant = {"0":"⁰", "1":"¹", "2":"²", "3": "³", "4":"⁴", "5":"⁵", "6":"⁶", "7":"⁷", "8":"⁸", "9":"⁹"}
string = str(number)
result = ""
for digit in string:
result += utf8_exposant[digit]
return result
result = ""
degree = 0
coefficients = self.coefficients
while coefficients != [] :
coef, coefficients = coefficients[-1], coefficients[0:-1]
result = "+ " +str(coef)+"x"+ from_number_to_string_exposant(degree) + result
degree+=1
return "<Polynom :" + result[1:] + " = 0 >"
#-----------------------------------------------------------------------------------------
def valueFor(self, value):
""" Returns ture or false depending on the fact that the given value is or not a polunom's solution. """
total_value = 0
degree = 0
coefficients = self.coefficients
while coefficients != [] :
coef, coefficients = coefficients[-1], coefficients[0:-1]
total_value += coef*(value**degree)
degree += 1
return total_value
#-----------------------------------------------------------------------------------------
def isSolution(self, value):
""" Returns true or false depending if the given value is a polynom solution or not """
return (self.valueFor(value) == 0)
#-----------------------------------------------------------------------------------------
def findSolution(self):
"""
Return a pair (p,q) that verify that p/q is a solution of this polynom. If no valid pair is find, return None.
Call to this function come with log printing.
"""
print("Search solution for ", self)
a0 = self.coefficients[-1]
aN = self.coefficients[0]
if a0 == 0 or aN == 0:
return None #algorithm is not applicable in this case.
else:
p_values = self._dividers(a0)
q_values = self._dividers(aN)
print("finded dividers of p :", p_values)
print("finded dividers of q :", q_values)
candidate_solutions = self._positiveAndNegativeCombinations(p_values,q_values)
print("(p,q) pairs to evaluate are : \n\t",candidate_solutions)
for candidate in candidate_solutions :
candidate_value = 1.0 * candidate[0] / candidate[1]
print("pair : ",candidate, " | value : ", candidate_value)
if self.isSolution(candidate_value):
print()
return candidate
else:
print("The pair ", candidate, "is invalid, replacing x by it leads to say that 0=", self.valueFor(candidate_value))
return None
#-----------------------------------------------------------------------------------------
if __name__ == "__main__":
poly = Polynom([2,1,-6])
print(poly.findSolution())
Execute it with python3 (or change the print calls).
Arthur.
https://code.activestate.com/recipes/577974-polynomial-factoring-using-rational-root-theorem/
Here's a working link.
from math import ceil
listOfFactors = lambda n: {i for i in range(1,ceil(abs(n)/2)+1) if n%i == 0}
def removeDuplicates(mylist):
if mylist:
mylist.sort()
last = mylist[-1]
for i in range(len(mylist)-2, -1, -1):
if last == mylist[i]:
del mylist[i]
else:
last = mylist[i]
return mylist
def polyRoots(polyListCoeff):
allFactors = set()
allFactorsListOld = list(allFactors.union(listOfFactors(polyListCoeff[0]),{polyListCoeff[0]},listOfFactors(polyListCoeff[-1]),{polyListCoeff[-1]}))
allFactorsListOld.extend([-1*i for i in allFactorsListOld])
allFactorsList = list()
for k in allFactorsListOld:
for j in allFactorsListOld:
allFactorsList.append(k/j)
allFactorsList = removeDuplicates(allFactorsList)
polyListCoeff.reverse()
roots = [i for i in allFactorsList if sum([pow(i,j)*polyListCoeff[j] for j in range(0,len(polyListCoeff))]) == 0]
factorList = list()
for i in roots:
if i<0:
factorList.append("(x+{})".format(-i))
else:
factorList.append("(x-{})".format(i))
return "".join(factorList)
if __name__ == "__main__":
polyRoots([1,0,-4])
# '(x+2.0)(x-2.0)'

Categories

Resources