Creating chains that loop on themselves - python

I want to have a number that breaks into 2 other numbers and checks for end conditions, breaking off each number until the conditions are met.
I came up with the following example to try to figure it out. Take a number, and break it into 2 more numbers: one is the original number multiplied by 2 and the other number is the original number divided by 3 without a remainder (//). This continues until a number is either greater than 100, equal to 6, or a square.
I want to record every chain that is made to be returned and printed out at the end. I can only do this by checking the second number in a chain currently and am not clever enough to figure out how to check both numbers. I want a new chain to be created every time the number is broken into 2 new numbers.
Currently, here is what I have:
import numpy as np
def check_conditions(number):
if number > 100:
return False
if number == 6:
return False
if np.sqrt(number) % 1 == 0:
return False
return True
def find_decay(number):
'''
Rule: Number is broken into two chains. First chain is
mulitplied by 2. Seconds chain is // 3. Same thing happens
to both chains unless end condition is met:
1. number is greater than 100
2. number is equal to 6
3. number is a perfect square
'''
master_chain = []
while check_conditions(number):
new_chain1 = [number * 2]
master_chain.append(new_chain1)
new_chain2 = [number // 3]
master_chain.append(new_chain2)
number = new_chain2[-1]
return master_chain
if __name__ == '__main__':
print find_decay(int(raw_input('Number: ')))
Does anyone have any ideas of ways to check conditions in a while loop for 2 separate numbers like?

This sort of problem typically lends itself to trees and/or recursion. However, the rate at which you're spawning new work is quite high compared to the rate at which you're going to satisfy the conditions. (ie while it won't take too many operations to exceed 100 for one product of each value, but low chances of finding a perfect square or exactly 6 on either fork)
Therefore, you'll want to set a max recursion depth for your implementation else you'll come up against the interpreter's limit (sys.getrecursionlimit()) and fail ugly.
I've provided a simple example of how you might do it below, recursively building a tree.
This is not particularly efficient though, and if you are interested in very long 'chains' then you may need to consider addressing this another way.
import sys
import numpy as np
class Node(object):
def __init__(self,number,parent):
self._parent = parent
self._number = number
self._satisfied = number > 100 or number == 6 or np.sqrt(number) % 1 == 0
self._left = None
self._right = None
self._depth = parent.depth + 1 if parent != None else 1
#property
def parent(self):
return self._parent
#property
def number(self):
return self._number
#property
def satisfied(self):
return self._satisfied
#property
def depth(self):
return self._depth
#property
def left(self):
return self._left
#left.setter
def left(self,value):
self._left = value
#property
def right(self):
return self._right
#right.setter
def right(self,value):
self._right = value
def print_all_chains(node,chain=[]):
if node.left is None:
chain.append(node.number)
print '{0}: {1}'.format(node.satisfied, chain)
else:
print_all_chains(node.left, chain[:] + [node.number])
print_all_chains(node.right, chain[:] + [node.number])
def build_tree(node, maxDepth):
if not node.satisfied and node.depth<maxDepth:
node.left = Node(node.number*2, node)
build_tree(node.left,maxDepth)
node.right = Node(node.number//3, node)
build_tree(node.right,maxDepth)
def find_decay(number):
root = Node(number,None)
build_tree(root,maxDepth=10)
print_all_chains(root)
if __name__ == '__main__':
find_decay(int(raw_input('Number: ')))

Using a simple Node class, this can give you an idea of the tree structure. This traverses the tree in level-order (which guarantees to find the shortest chain):
from collections import deque
import numpy as np
def check_conditions(number):
return number > 100 or number == 6 or np.sqrt(number) % 1 == 0
class Node():
def __init__(self, value, parent=None):
self.value, self.parent = value, parent
def chain(self):
node = self
while node:
yield node.value
node = node.parent
def find_decay(number):
agenda = deque([Node(number)])
while agenda:
node = agenda.popleft() # use pop() for depth-first
num = node.value
if check_conditions(num):
return list(node.chain())
agenda.append(Node(num//3, parent=node))
agenda.append(Node(num*2, parent=node))
if __name__ == '__main__':
for x in find_decay(int(raw_input('Number: '))):
print x,
37: 37 12 4

Related

How do I set the prev of my first node to the next of my last node to create a circular doubly linked list?

I am creating the Josephus problem using a circular doubly linked list. I am getting an Attribute error, which I assume is because my current_node (first node) does not have a .prev yet.
I understand that the prev of my first node should point to the next of my last node to create a circular doubly linked list.
Can someone guide me on whether I have correctly identified the error? If yes, how can I rectify it?
If not, then what are the other ways I can correct the error?
#Initialize the node
def __init__(self, data):
self.data = data
self.next = None
self.prev = None
def remove(self, n):
print("Student " +str(n)+ " was removed")
class Circle:
# Initializing the DLL
def __init__(self):
self.head = None
self.tail = None
#Inserting elements 2 to n in the dll
def insert_after(self, x, data):
y = Student(data) # make a new Node object.
z = Student(data)
z = x.next
y.prev = x
y.next = z
x.next = y
z.prev = y
def josephus_solution(self, dllist, n, k):
no_of_active_nodes = n
current_node = Student(1)
#last_node = Student(n)
#print(current_node.prev)
for i in range(2, n + 1):
dllist.insert_after(current_node, i)
count = 0
#print(current_node.data)
while (current_node.next != current_node.prev):
#print(current_node.next.prev)
current_node = current_node.next
count += 1
#print(current_node.data)
if (count == k):
current_node.remove(current_node.data)
current_node.prev.next = current_node.next
current_node.next.prev = current_node.prev
count = 0
no_of_active_nodes -= 1
#print(no_of_active_nodes)
if (no_of_active_nodes == 1):
print("Student" + str(current_node.data) + "Recieves the scholarship")
return current_node.data
dllist = Circle()
n = 5 #int(input('Input number of people (n): '))
k = 2 #int(input('The nth person will be executed. Input k: '))
ans = dllist.josephus_solution(dllist, n, k)
Error
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
/tmp/ipykernel_24/3762059582.py in <module>
54 n = 5 #int(input('Input number of people (n): '))
55 k = 2 #int(input('The nth person will be executed. Input k: '))
---> 56 ans = dllist.josephus_solution(dllist, n, k)
/tmp/ipykernel_24/3762059582.py in josephus_solution(self, dllist, n, k)
32 #print(current_node.prev)
33 for i in range(2, n + 1):
---> 34 dllist.insert_after(current_node, i)
35 count = 0
36 #print(current_node.data)
/tmp/ipykernel_24/3762059582.py in insert_after(self, x, data)
24 x.next = y
25
---> 26 z.prev = y
27
28 def josephus_solution(self, dllist, n, k):
AttributeError: 'NoneType' object has no attribute 'prev'
The direct reason for the error is that z is None and your code can therefore not access any prev attribute of it in the line z.prev = y. The cause is that when the first node is created, its prev and next attributes are initialised to None, and when this node is passed as x argument to insert_after, then with z = x.next a None value is assigned to z.
There are several issues with your approach:
In insert_after it makes no sense to call Student twice, since you want to insert one node, not two
Although your Circle class has a head and a tail attribute, they are never used after their initialisation to None, so there is actually nothing in the Circle instance that helps you to maintain the list. You might as well define all methods on the Student class.
The while condition in josephus_solution is probably intended to test that only one node remains in the list, but it actually verifies whether there are two left. Admittedly, this works when current_node is a node that was just deleted, but then the returned data is the data of the deleted node and not the remaining active node, and current_node is not a just-deleted node, then this condition will make the loop exit when it still has two active nodes.
Some other remarks:
As you call josephus_solution as a method, it should not be necessary to also pass an instance as argument. self is already available to that method
I would split the creation of the nodes into a separate -- generic -- method, and let josephus_solution work on an existing linked list.
As in a circular list none of the next or prev attributes should ever be None, you'll make things easier when initialising them to self instead of None.
The remove method should not be responsible for printing a message, but should on the other hand be responsible for rewiring the prev and next references. At any rate, there is no need for this method to get an argument, as it knows its own attributes through self.
In the original Josephus problem, the first node is numbered as 1, so with k=2 the first node to remove is the one with number 2. Your code would first delete the one with number 3. To align with the original problem, move the current_node = current_node.next statement to be the last line in the body of the loop. This also helps to be sure that current_node is never a just-deleted node when the while condition is evaluated.
As your while condition already takes care of when to stop, there should be no reason to keep track with a no_of_active_nodes variable.
There are more remarks to make, but these are what I believe the most important.
Here is a correction of your code, taking all of the above into account:
class Student:
def __init__(self, data):
self.data = data
# Better make prev/next self references to avoid None:
self.next = self
self.prev = self
def remove(self):
# Perform the remove here
self.prev.next = self.next
self.next.prev = self.prev
def insert(self, data):
# Should only create one node, not two
node = Student(data)
node.next = self
node.prev = self.prev
self.prev = node.prev.next = node
# Handy method to create a circular list from values
#classmethod
def fromiterator(cls, iterator):
head = cls(next(iterator))
for value in iterator:
head.insert(value)
return head
def josephus_solution(self, k):
count = 0
current_node = self
while current_node.next is not current_node: # This is how to test for 1 node
count += 1
if count == k:
current_node.remove() # No argument
print("Student " +str(current_node.data) + " was removed")
count = 0
current_node = current_node.next # do this at the end of the iteration
# No need to keep track of active nodes ...
return current_node
# These are the parameters of the "original" problem (see Wikipedia)
n = 41
k = 3
dllist = Student.fromiterator(iter(range(1, n+1)))
# Don't pass the list as argument: it already is available as `self`
dllist = dllist.josephus_solution(k)
# Perform output in the main code, not inside method
print("Student " + str(dllist.data) + " recieves the scholarship")

Stacks and optimization - example from hackerrank

I have a question concerning stacks in Python. I tried to solve a Maximum Element task in Hackerrank:
You have an empty sequence, and you will be given N queries. Each query
is one of these three types:
1 x -Push the element x into the stack.
2 -Delete the element present at the top of the stack.
3 -Print the maximum element in the stack.
The first line of input contains an integer, N. The next N lines each
contain an above mentioned query. (It is guaranteed that each query is
valid.)
To solve it I wrote something like this:
class Stack:
def __init__(self):
self.items = []
def push(self, item):
self.items.append(item)
def pop(self):
return self.items.pop()
def maxEl(self):
return max(self.items)
s = Stack()
for i in range(int(input())):
n = input().split()
if n[0] == '1':
s.push(int(n[1]))
elif n[0] == '2':
s.pop()
else:
print(s.maxEl())
It works, but too slow apparently and I pass only 18 out of 28 testcases (because of timeout). I have found a similar solution, and it is fast enough, but I don't understand why:
class Stack:
def __init__(self):
self.arr = [0]
self.max = [0]
def push(self, data):
self.arr.append(data)
if self.max[-1] <= data:
self.max.append(data)
def pop(self):
if self.arr[-1] == self.max[-1]:
self.max.pop()
self.arr.pop()
N = int(input())
s = Stack()
for _ in range(N):
x = str(input())
if x[0] == '1':
s.push(int(x[2:]))
elif x[0] == '2':
s.pop()
else:
print(s.max[-1])
Can somebody explain me why my code isn't performing well? Thank you.
The two solutions are pretty similar, except for the code that returns the maximum element in the stack.
In your solution you use the max() function:
def maxEl(self):
return max(self.items)
This runs in O(n) time, since max() must check every element in the worst case.
In the other solution maximum values are stored in yet another stack, so getting the current maximum value is just an index operation, which runs in O(1) time:
s.max[-1]
There's also some cost associated with updating the stack of maximums on each push/pop, but those operations are still constant time.
Given the definition of the problem even the working solution is doing way too much. More specifically you need to remember ONLY the max in the stack; something like
s = []
for _ in range(N):
x = str(input())
if x[0] == '1':
v = int(x[2:])
s.append(v if len(s) == 0 else max(v, s[-1]))
elif x[0] == '2':
s.pop()
else:
print(s[-1])
should be sufficient.

Finding the turning point of a function

I'd like to the first value that outputs True for my function. I currently have a search that works fine, but I think is still a bit inefficient. Could anyone suggest a better binary search? My code is below, simplified.
guess = 2
limits = [2, 2**35] #The search area
while True:
if myFunction(guess) == False:
limits[0] = max(limits[0], guess) #Limit the search area
guess *= 2
else:
limits[1] = min(limits[1], guess) #Limit the search area
guess = int((limits[0] + limits[1])/2) #The guess is the midpoint of the search area
if myFunction(guess) == True and myFunction(guess-1) == False:
return guess
This is the classical problem of finding a level-crossing of a monotonically increasing or decreasing function. As you guessed, it is solvable by binary search. Your code has some bugs, which is not surprising:
Although the idea is simple, implementing binary search correctly requires attention to some subtleties about its exit conditions and midpoint calculation.
So, you should avoid writing your own binary search when possible. Fortunately, Python offers a library module bisect which can do the job for you.
from bisect import bisect_left
MIN = 2
MAX = 2**35
def search(f):
# Finds the first position of `True` in `f`
return bisect_left(f, True, lo=MIN, hi=MAX + 1)
Don't be confused by the fact that bisect only works with indexable objects: there is no need to create a list with 2**35 elements. You can use a generator object instead using the __getitem__ syntax. To do that, encapsulate your function in a class and define the getter method that would return False for all argument values on the left side of the point of interest and True otherwise.
def myFunction1(index):
return index >= 1456
def myFunction2(index):
return index >= 2
def myFunction3(index):
return index >= MAX - 1
class F:
def __init__(self, f):
self.f = f
def __getitem__(self, index):
return self.f(index)
# testing code
print(search(F(myFunction1))) # prints 1456
print(search(F(myFunction2))) # prints 2
print(search(F(myFunction3))) # prints MAX - 1

Calculating the length of a Linked-List in Python

I have a linked-list and I want to calculate it's length with a function, here's my definition :
class ListNode(object):
def __init__(self, x):
self.val = x
self.next = None
Try this function:
def length(lst):
r = 0
while lst:
lst = lst.next
r += 1
return r # 'r' being the length
It works by moving forward along the list counting the number of nodes observed until a None link is encountered.
You can simply set the head node to a variable, and continuous count until you hit the point where temp == NULL
def height(list):
temp=list.head
count=0
while temp:
count+=1
temp=temp.next
return count

Python binary search recursive if possible

class SortedList:
theList = []
def add(self, number):
self.theList.append(number)
return self.theList
def remove(self, number):
self.theList.remove(number)
return self.theList
def printList(self):
return print(self.theList)
def binarSearch(self, number):
middle = (len(self.theList)//2)
end = len(self.theList)
if end != 0:
if int(self.theList[middle]) == int(number):
return print("The number is found in the list at place",middle+1)
elif int(self.theList[middle]) < int(number):
self.theList = self.theList[middle:]
return self.binarSearch(number)
elif int(self.theList[middle]) > int(number):
self.theList = self.theList[:middle]
return self.binarSearch(number)
else:
return print("The list is empty")
sorted = SortedList() #create a SortedList object
sorted.add("1")
sorted.add("2")
sorted.add("3")
sorted.add("4")
sorted.add("5")
sorted.add("6")
sorted.printList()
sorted.binarSearch(3)
I cannot use additional parameters I mut use only self and number. I want to make it recursive but if it is hard you can answer as normal.
This code works good until the number 4. When I give 4 for searching it says it is in place 2 and it continues saying two after 4. I have tried adding other numbers but it is same
Python already has a great module bisect which performs a binary search for sorted lists:
import bisect
l = [2,3,1,5,6,7,9,8,4]
print(bisect.bisect(l, 4)) # Output: 3
Familiarize yourself with this library:
https://docs.python.org/3.5/library/bisect.html
Just a hint: You can use additional parameters if you give them default values. Your method signature would look like this:
def binarSearch(self, number, start=0, end=len(self.theList)):
So it could still be called like sorted.binarySearch(5) but would internally be able to pass the state correctly.

Categories