Please note, this is a STATIC archive of website www.tutorialspoint.com from 11 May 2019, cach3.com does not collect or store any user information, there is no "phishing" involved.
Tutorialspoint

Execute Python-3 Online

# Week 4 Cohort Questions

# Get DNA Base Count


def get_base_counts(dna):
    dna_list = list(dna)
    ans = {}
    bases = ['A', 'C', 'G', 'T']
    for k in dna_list:
        if k == 'A' or k == 'C' or k == 'G' or k == 'T':
            pass
        else:
            return 'The input DNA string is invalid'
    for i in bases:
        count = 0
        for a in dna_list:
            if a == i:
                count += 1
        ans[i] = count
    return ans

def leap_year(year):
  if year%4 != 0:
      return False
  elif year%100 != 0:
      return True
  elif year%400 != 0:
      return False
  else:
      return True


def day_of_week_jan1(year):
  day = (1 + 5*((year - 1)%4)+4*((year - 1)%100)+6*((year - 1)%400))%7
  return day

def num_days_in_month(month_num, leap_year):
  num = 0
  if month_num == 2:
    if leap_year == True:
      num = 29
    elif True:
      num = 28
  if month_num == 1 or month_num == 3 or month_num == 5 or month_num == 7 or month_num == 8 or month_num == 10 or month_num == 12:
      num = 31
  elif month_num == 4 or month_num == 6 or month_num == 9 or month_num == 11:
      num = 30
  return num

def day_of_year(month, leap_year):
  day = 0
  for i in range(1, month+1):
        day += num_days_in_month(i-1, leap_year)
  return day

def first_day_of_month(year, month):
  day = day_of_week_jan1(year) + (day_of_year(month, leap_year(year)))%7
  while day >= 7:
        day -= 7
  return day

def construct_cal_month(month_num, first_day_of_month, num_days_in_month):
    Month = month[month_num]
    num = num_days_in_month
    num_list = list(range(1, num + 1))
    day_list = list()
    for i in num_list:
        day_list.append("{0:3d}".format(i))
    line = ""
    p = 0
    if first_day_of_month == 7:
      line = line
    else:
      line = line + "   " * first_day_of_month
    while p < len(day_list):
        line += day_list[p]
        p += 1
    linelist = list(line)
    linelist.insert(21, "+")
    linelist.insert(43, "+")
    linelist.insert(65, "+")
    linelist.insert(87, "+")
    linelist.insert(109 ,"+")
    breaker = ""
    linestring = breaker.join(linelist)
    #print(linestring)
    newlist = linestring.split("+")
    #print(newlist)
    result = []
    result.append(Month)
    for i in newlist:
        result.append(i)
    if result[len(result)-1] == "":
        result.pop()
    return result


def construct_cal_year(year):
    yearlist = []
    yearlist.append(year)
    for i in range(1,13):
      yearlist.append(construct_cal_month(i,first_day_of_month(year, i), num_days_in_month(i, leap_year(year))))
    return yearlist

def display_calendar(year):
  output = construct_cal_year(year)
  outputstring = ""
  for i in output:
    if i == year:
      pass
    else:
      outputstring += i[0]+"\n"
      outputstring += "  S  M  T  W  T  F  S\n"
      k = 1
      while k < len(i):
        outputstring += i[k] +"\n"
        k += 1
      if len(i) > 5:
        outputstring += "\n"
  outputstring = outputstring.rstrip()
  return outputstring

# Game: Craps is a popular dice game played in casinos. Write a program allowing you to play a variation of the game. This is how the game would be played:
    # (a) For the first round, roll two dice. Each die has six faces, and each face reflects a value from 1 to 6. Check the sum of the two dice.
        # 1. If the sum is 2, 3 or 12 (called craps), you lose
        # 2. If the sum is 7 or 11 (called natural), you win
        # 3. If the sum is another value (i.e. 4,5,6,8,9, or 10), you earn points equal to the sum obtained.
    # (b) Continue to roll the dice until the sum of both dice is either a 7 or the points obtained in the first round.
        # 1. If 7 is rolled, you lose
        # 2. If the sum obtained is equal to the points you obtained in the first round, you win
        # 3. For other sums, continue to roll the dice

#Your program acts as a single player, and should print the output you see below when the
#various conditions are met. The function play_craps() should return 0 if you lose and
#1 if you win. The main program is given here, together with the sub functions you will
#need to dene. Hint: if you are unsure how to start, take a look at the main function
#play_craps() and see how the sub functions are being called by the main function, to get
#a better idea of what is required of each sub function.

import random as rng

craps=set([2,3,12])
naturals=set([7,11])
rng.seed(a = None)

def roll_two_dices():
    n1 = rng.randrange(1, 7)
    n2 = rng.randrange(1, 7)
    return [n1, n2]

def print_lose():
    print('You lose')

def print_win():
    print('You win')

def print_point(p):
    print('Your points are' ,p)

def is_craps(n):
    for i in craps:
        if n == i:
            return True
    return False

def is_naturals(n):
    for i in naturals:
        if n == i:
            return True
    return False


# ATTENTION!
# You shouldn't need to edit the function below

def play_craps():
    point=-1
    while True:
        n1,n2=roll_two_dices()
        sumn=n1+n2
        print ('You rolled %d + %d = %d'%(n1,n2,sumn))   #initial points obtained here
        if point==-1:              #At the initialisation of point == -1 (see line 29)
            if is_craps(sumn):  #if the sum is in the set of craps
                print_lose()     #then it is an immediate loss
                return 0
            elif is_naturals(sumn):   #if the sum is in the set of naturals
                print_win()     #then it is an immediate  win
                return 1
            point=sumn
            print_point(point)
        else:
            if sumn==7:
                print_lose()
                return 0
            elif sumn==point:
                print_win()
                return 1   #goal of the game is to keep rolling dice until the sum == points obtained

# Calendar Year

month = {1:"January", 2:"February", 3:"March",4:"April", 5:"May", 6:"June", 7:"July", 8:"August", 9:"September", 10:"October", 11:"November", 12:"December"}

def leap_year(year):
  if year%4 != 0:
      return False
  elif year%100 != 0:
      return True
  elif year%400 != 0:
      return False
  else:
      return True


def day_of_week_jan1(year):
  day = (1 + 5*((year - 1)%4)+4*((year - 1)%100)+6*((year - 1)%400))%7
  return day

def num_days_in_month(month_num, leap_year):
  num = 0
  if month_num == 2:
    if leap_year == True:
      num = 29
    elif True:
      num = 28
  if month_num == 1 or month_num == 3 or month_num == 5 or month_num == 7 or month_num == 8 or month_num == 10 or month_num == 12:
      num = 31
  elif month_num == 4 or month_num == 6 or month_num == 9 or month_num == 11:
      num = 30
  return num

def day_of_year(month, leap_year):
  day = 0
  for i in range(1, month+1):
        day += num_days_in_month(i-1, leap_year)
  return day

def first_day_of_month(year, month):
  day = day_of_week_jan1(year) + (day_of_year(month, leap_year(year)))%7
  while day >= 7:
        day -= 7
  return day

def construct_cal_month(month_num, first_day_of_month, num_days_in_month):
    Month = month[month_num]
    num = num_days_in_month
    num_list = list(range(1, num + 1))
    day_list = list()
    for i in num_list:
        day_list.append("{0:3d}".format(i))
    line = ""
    p = 0
    if first_day_of_month == 7:
      line = line
    else:
      line = line + "   " * first_day_of_month
    while p < len(day_list):
        line += day_list[p]
        p += 1
    linelist = list(line)
    linelist.insert(21, "+")
    linelist.insert(43, "+")
    linelist.insert(65, "+")
    linelist.insert(87, "+")
    linelist.insert(109 ,"+")
    breaker = ""
    linestring = breaker.join(linelist)
    #print(linestring)
    newlist = linestring.split("+")
    #print(newlist)
    result = []
    result.append(Month)
    for i in newlist:
        result.append(i)
    if result[len(result)-1] == "":
        result.pop()
    return result


def construct_cal_year(year):
    yearlist = []
    yearlist.append(year)
    for i in range(1,13):
      yearlist.append(construct_cal_month(i,first_day_of_month(year, i), num_days_in_month(i, leap_year(year))))
    return yearlist

def display_calendar(year):
  output = construct_cal_year(year)
  outputstring = ""
  for i in output:
    if i == year:
      pass
    else:
      outputstring += i[0]+"\n"
      outputstring += "  S  M  T  W  T  F  S\n"
      k = 1
      while k < len(i):
        outputstring += i[k] +"\n"
        k += 1
      if len(i) > 5:
        outputstring += "\n"
  outputstring = outputstring.rstrip()
  return outputstring

# Factorial

def fact_rec(n):
    if n == 1 or n == 0:
        return 1
    else:
        return n*fact_rec(n-1)

# Modular Design

# NB: you do not need to submit the 'get_data' function

def get_data():
    integer1 = input('Enter first integer: ')
    integer2 = input('Enter second integer: ')
    return integer1, integer2

def extract_values(values):
    str_list = values.split()
    for i in range(len(str_list)):
        str_list[i] = int(str_list[i])
    int_tuple = (str_list[0], str_list[1])
    return int_tuple

def calc_ratios(values):
        try:
            return values[0]/values[1]
        except ZeroDivisionError:
            return None


# Week 6 Cohort Questions

# Reverse String


def reverse(string):
    r = []
    for i in range(len(string)-1, -1, -1):
        r.append(string[i])
    return ''.join(r)

# Check Password Validity


def check_password(password):
    if len(password)<8:
        return False
    for i in password:
        if 48<=ord(i)<=57 or 65<=ord(i)<=90 or 97<=ord(i)<=122:
            pass
        else:
            return False
    count = 0
    for i in password:
        if 48<=ord(i)<=57:
            count += 1
    if count < 2:
        return False
    return True

# Longest Common Prefix


def longest_common_prefix(string1, string2):
    ans = ""
    a = 0
    while True:
        if string1[a] == string2[a]:
            ans += string1[a]
        elif string1[a] != string2[a]:
            break
        if len(ans)==len(string1) or len(ans)==len(string2):
            break
        a += 1
    return ans


# Week 6 Homework

# Strings: Binary to Decimal


def binary_to_decimal(binstr):
    return int(binstr,2)

# Compressed string to uncompressed string


def uncompress(string):
    numbers = []
    strings = []
    for i in string:
        if i.isdigit():
            numbers.append(i)
        else:
            strings.append(i)
    ans = ''
    k = 0
    for a in numbers:
        ans += int(a)*strings[k]
        k += 1
    return ans

# DNA Base counts


def get_base_counts2(dna):
    dna_list = list(dna)
    ans = {}
    bases = ['A', 'C', 'G', 'T']
    for k in dna_list:
        if k == 'A' or k == 'C' or k == 'G' or k == 'T':
            pass
    for i in bases:
        count = 0
        for a in dna_list:
            if a == i:
                count += 1
        ans[i] = count
    if len(ans)==0:
        return 'The input DNA string is invalid'
    return ans

# Files and I/O

f = open('text_files/constants.txt', 'r')

def get_fundamental_constants(f):
    ff = f.readlines()
    ans = {}
    for i in ff:
        nameget = False
        for a in i.split():
            isname = True
            if nameget == False:
                for b in a:
                    if 65<=ord(b)<=90 or 97<=ord(b)<=122:
                        continue
                    else:
                        nameget = True
                        isname = False
                        break
            if isname==True and nameget==False:
                name = a
            try:
                num = float(a)
            except ValueError:
                pass
            try:
                num = int(a)
            except ValueError:
                pass
        try:
            ans[name] = num
        except UnboundLocalError:
            pass
    return ans

# Files: Process Scores

f = open('text_files/scores.txt')

def process_scores(f):
    ff = f.readline()
    total = 0
    for i in ff.split():
        total += float(i)
    fff = ff.split()
    avg = total/(len(fff))
    return total, avg

#  Week 8 Cohort Questions


## CS1 COORDINATE CLASS##
class Coordinate:
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def magnitude(self):
        return (self.x ** 2 + self.y ** 2) ** .5

    def translate(self, dx, dy):
        self.x += dx
        self.y += dy
        return self

    def __eq__(self, other):
        return (self.x, self.y) == (other.x, other.y)



## CS2 CELCIUS CLASS ##
class Celsius:
    def __init__(self, temperature=0):
        self.C = temperature

    def to_fahrenheit(self):
        return (9 / 5. * self.C) + 32

    def get_temperature(self):
        return self.C

    def set_temperature(self, value):
        if value > -273:
            self.C = value
        else:
            self.C = -273

    temperature = property(get_temperature, set_temperature)


## CS3 STOPWATCH CLASS ##
import time


class StopWatch:
    def __init__(self):
        self.start_time = time.time()
        self.end_time = -1

    def start(self):
        self.start_time = time.time()
        self.end_time = -1

    def stop(self):
        self.end_time = time.time()

    def elapsed_time(self):
        if self.end_time == -1:
            return None
        elif self.end_time - self.start_time < 0:
            return None
        else:
            return round(self.end_time - self.start_time, 1)



## CS4 STRAIGHTLINE CLASS ##
class Line:
    def __init__(self, c0, c1):
        self.c0 = c0
        self.c1 = c1

    def __call__(self, x):
        return float(self.c0 + self.c1 * x)

    def table(self, L, R, n):
        s = ''
        if R == L:
            n = 1
        import numpy as np
        for x in np.linspace(L, R, n):
            y = self(x)
            s += '%10.2f%10.2f\n' % (x, y)
        return 'Error in printing table' if s == '' or L > R else s


line = Line(3, 4)
print(line(2))
print(Line.table(line, 3, 2, 15))


## HW1 TIME CLASS ##
class Time:
    def __init__(self, hours=0, minutes=0, seconds=0):
        self._hours = hours % 24
        self._minutes = minutes % 60
        self._seconds = seconds % 60

    def get(time):
        minutes = time._hours * 60 + time._minutes
        seconds = minutes * 60 + time._seconds
        return seconds

    def __str__(self):
        return "Time: %d:%d:%d" % (self._hours, self._minutes, self._seconds)

    def seconds(self, seconds):
        self._hours = seconds // 3600 % 24
        seconds -= self._hours * 3600
        self._minutes = seconds // 60 % 60
        seconds -= self._minutes * 60
        self._seconds = seconds % 60
        return seconds

    elapsed_time = property(get, seconds)


t = Time(100, 19, 10)
print(t.elapsed_time)
t.elapsed_time = 45550
print(t.elapsed_time)
print(t)


## HW2 BANK ACCOUNT CLASS ##
class Account:
    def __init__(self, owner, account_number, amount):
        self._owner = owner
        self._account_number = account_number
        self._balance = amount

    def deposit(self, amount):
        self._balance += amount
        return self

    def withdraw(self, amount):
        self._balance -= amount
        return self

    def __str__(self):
        return '%s, %s, balance: %s' % (self._owner, self._account_number, self._balance)



## HW3 NUMERICAL DIFFERENTIATION ##
class Diff:
    def __init__(self, f, h=1e-4):
        self.f = f
        self.h = float(h)

    def __call__(self, x):
        f, h = self.f, self.h
        return (f(x + h) - f(x)) / h




## HW4 POLYNOMIAL CLASS ##
class Polynomial:
    def __init__(self, coefficients):
        self.coeff = coefficients

    def zip_longest(a, b):
        for i in range(max(len(a), len(b))):
            if i >= len(a):
                yield [b[i]]
            elif i >= len(b):
                yield [a[i]]
            else:
                yield [a[i], b[i]]
            i += 1

    def __add__(self, target):
        return Polynomial([sum(i) for i in Polynomial.zip_longest(self.coeff, target.coeff)])

    def __sub__(self, target):
        return Polynomial([sum(i) for i in Polynomial.zip_longest(self.coeff, [-i for i in target.coeff])])

    def __call__(self, x):
        val = []
        for i, j in enumerate(self.coeff):
            val.append(j * x ** i)
        return sum(val)

    def __mul__(self, target):
        ls = [0] * (len(self.coeff) * len(target.coeff) - 1)
        for i, j in enumerate(self.coeff):
            for k, l in enumerate(target.coeff):
                ls[i + k] += j * l
        end = len(ls)
        for i in ls[::-1]:
            if i != 0.:
                break
            else:
                end -= 1
        return Polynomial(ls[0:end])

    def differentiate(self):
        ls = [0] * (len(self.coeff) - 1)
        for i, j in enumerate(self.coeff):
            ls[i - 1] += i * j
        self.coeff = ls

    def derivative(self):
        deriv = []
        power = 1
        for i in range(1, len(self.coeff)):
            deriv.append(self.coeff[i] * power)
            power += 1
        return Polynomial(deriv)


p1 = Polynomial([1, -1])
p2 = Polynomial([0, 1, 0, 0, -6, -1])
p3 = p1 + p2
print(p3.coeff)
[1, 0, 0, 0, -6, -1]
p4 = p1 * p2
print(p4.coeff)
[0, 1, -1, 0, -6, 5, 1]
p5 = p2.derivative()
print(p5.coeff)
[1, 0, 0, -24, -5]
p = Polynomial([1, 2, 3])
q = Polynomial([2, 3])
r = p - q
print(r.coeff)
[-1, -1, 3]
r = q - p
print(r.coeff)
[1, 1, -3]
P = Polynomial([1, -1])
Q = Polynomial([0, 1, 0, 0, -6, -1])
R = P - Q
print(R.coeff)
print(R(3))
E = Polynomial([1, 3, 5, 7, 9])
E.differentiate()
print(E.coeff)



## EX1 FUNCTION CLASS ##
from math import *


class F:
    def __init__(self, a, w):
        self.a = a
        self.w = w

    def __call__(self, x):
        return (exp(-self.a * x)) * sin(self.w * x)



## EX2 STRAIGHTLINE ALTERNATIVE ##
class Line0:
    def __init__(self, p1, p2):
        self.p1 = p1
        self.p2 = p2

    def __call__(self, x):
        m = (self.p1[1] - self.p2[1]) / (self.p1[0] - self.p2[0])
        c = self.p1[1] - m * self.p1[0]
        return m * x + c

#  Week 9

## CS1 COKE ##
from libdw import sm
class CM(sm.SM):
    start_state = 0
    def get_next_values(self,state, inp):
        if state == 0 and inp == 50:
            return (1, (50, '--',0))
        if state == 1 and inp == 50:
            return (0, (0, 'coke',0))
        if state == 1 and inp == 100:
            return (0, (0,'coke',50))
        if state == 0 and inp == 100:
            return (0, (0,'coke',0))
        else:
            return (0, (0,'--',inp))


## CS2 SIMPLE ACCOUNT ##
from libdw import sm


class SimpleAccount(sm.SM):
    def __init__(self, start_deposit):
        self.M = start_deposit

    def get_next_values(self, state, inp):
        state = self.M
        if self.M < 100 and inp < 0:
            self.M = state + inp - 5
            return self.M, self.M
        else:
            self.M = state + inp
            return self.M, self.M


## HW1 COMMENTS ##
from libdw import sm
class CommentsSM(sm.SM):
    start_state = 0
    def get_next_values(self, state, inp):
        if state == 0 and inp != '#':
         return (0, None)
        if state == 0 and inp == '#':
         return (1, inp)
        if state == 1 and inp !='\n':
         return (1, inp)
        if state == 1 and inp == '\n':
         return (0, None)

## HW2 FIRST WORD ##
from libdw import sm
class FirstWordSM(sm.SM):
    start_state = 0
    def get_next_values(self, state, inp):
        if state == 0 and inp in [' ','\n']:
         return (0, None)
        if state == 0 and inp not in [' ','\n']:
         return (1, inp)
        if state == 1 and inp == ' ':
         return (2, None)
        if state == 1 and inp != ' ':
         return (1, inp)
        if state == 2 and inp !='\n':
         return (2, None)
        if state == 2 and inp == '\n':
         return (0, None)



# Consider a state machine with: Q1:
# inputs: 0, 1, 2
# states: 0, 1, 2, 3
# outputs: 0, 1, 2, 3
# initial state: 0
# transition function:
#
# States 00 0 0 1 2
# old state: 0 1 3 0
# old state: 1 2 0 0
# old state: 2 3 1 0
# old state: 3 0 2 0
# output function: same as transition function
# It maybe helpful for you to draw a state diagram of this machine, to visualize its operation.
#
# What is the best description of this machine?
#
# Ans:
#
# It counts forward and backward mod 4, and has a reset input. Q2:
# Input: [0,0,2,0,0,0,1,1,1] Output: [1,2,0,1,2,3,2,1,0] Q3:
# Output: [3,2,0,1,2,3] Input: [1,1,2,0,0,0]


# Midterms 2016

# Q.1 [10 points]
# State the similarities and differences between list and dictionary data structures. State what kind of data is suitable for each data structure and give examples.
#
# Similarities:
#
# Both lists and dictionaries are mutable.
# Both lists and dictionaries can be generated via comprehensions.
# Both lists and dictionaries are part of the 'collections' module.
# Both lists and dictionaries contain elemets that can be of any combination of data types.
# Differences:
#
# Lists mantain order of thier elements while dictionaries are unordered.
# Lists allow duplication while dictionary items are unique.
# List items are accessed by index while dictionary items are accessed by keys.
# Lists are implemented internally as variable-length arrays while dictionaries are implemented internally as resizable hash tables.
# Examples:
#
# Use dictionaries to associate values with keys for efficient 'by key' seaching:
# Telephone { 'Drake' : '1-800-468-546325464' }
# Address { 'Sponge Bob' : '124 Conch Street, Bikini Bottom, Pacific Ocean' }
# Translation { "EN" : "DE", "red" : "rot", "green" : "grün", "blue" : "blau", "yellow":"gelb" }
# Use lists for an ordered sequence of items:
# Race standings [ 'me', 'me', 'me again', 'you' ]
# Instructions [ 'To add in pot:', 'shark', 'cuttlefish', 'squid', 'mantis shrimp', 'anemone' ]
# Sensor readings [ '0.21532', '0.98765', '1.12353', '9999.99999' ]

# Q.2 [10 points]
# A student wrote the following Python program to instruct the robot to move. From the written code, answer the following:
#
# a) Explain briefly what the student tries to do with the code. Give some numerical values in your explanation related to the speed of the robot. [3 pts]
#
# b) How many times is the function increasePower()being called? Explain briefly how you arrived at your answer.[3 pts]
#
# c) Explain what will happen if the sleep(2) statement is removed from the code? [4 pts]

from time import sleep
from eBot import eBot

ebot = eBot.eBot()
ebot.connect()
power = 0.1
def increasePower(power):
    power += 0.1
    return power
while power <= 1:
    ebot.wheels(power,power)
    sleep(2)
    power=increasePower(power)
ebot.disconnect()

#
# a) The student is trying to accelerate the robot from the speed equivalent of power = 0.1 to that of power = 1 at increments of 0.1 every 2 seconds.
#
# b) The functions increasePower() is called 9 times. This is as there are 9 intervals between 0.1 and 1 with increment of 0.1.
#
# c) The robot will accelerate from the speed equivalent of power 0.1 to that of power= 1 instantly. This is as without the sleep(2) command, there is virtually no delay between the sucessive increasePower() commands until the while condition of power = 1 is met.
#
# Q.3 [5 points]
# Write a function norm(z1,z2,z3) that returns the Euclidean norm in 3-dimensional complex space  $C^{3}$, where z1, z2,and z3 are complex numbers. The norm is calculated using the following formula:
#
# $$ ||\ Z\ ||\ :=\ \sqrt{z_1 \overline{z_1}\ +\ z_2 \overline{z_2}\ +\ z_3 \overline{z_3}}  $$
# The function should return a real number rather than a complex number, and the number should be rounded to three decimal places.

#from numpy.linalg import norm
#normal = lambda z1, z2, z3: round(norm([z1,z2,z3]),3)
def normal(z1,z2,z3):
    return round(abs((z1*z1.conjugate() +z2*z2.conjugate()+ z2*z2.conjugate())**.5), 3)

## TEST CASES ##
print ('test 1')
z1=1+3j
z2=-1+3j
z3=-1-3j
ans=normal(z1,z2,z3)
print (ans)
print ('test 2')
z1=1+2j
z2=-1+2j
z3=-1-2j
ans=normal(z1,z2,z3)
print (ans)
print ('test 3')
z1=1+1j
z2=-1+1j
z3=-1-1j
ans=normal(z1,z2,z3)
print (ans)


# Q.4 [10 points]
# Write a function factors(n) that takes an integer n as an input and returns a list that includes all
# the positive number factors of n, where n ≥ 1. The output returns the list of all the factors in an ascending order.

def factors(n):
    return [i for i in range(1,n+1) if n % i == 0]

## TEST CASES ##
print ('test 1')
ans=factors(6)
print (ans)
print ('test 2')
ans=factors(12)
print (ans)
print ('test 3')
ans=factors(7)
print (ans)
print ('test 4')
ans=factors(15)
print (ans)
print ('test 5')
ans=factors(21)
print (ans)
print ('test 6')
ans=factors(1)
print (ans)
print ('test 7')
ans=factors(9)
print (ans)


# Q.5 [10 points]
# Write a function combinations(n1,n2) that takes in two integers n1 and n2, and returns a list of tuples of all
# possible combinations of two integers, both of which range from n1 to n2. The function also returns a second output
# which is the total number of all combinations. The output combinations must start from the lower number to the higher
# number as shown in the expression below and in the output test cases. You are not allowed to use any built-in function
# that gives the same result immediately.

def combinations(a,b):
 return([] if any([a,b])==0 else [(i,j) for i in range(a,b) for j in range(a+1,b+1) if i<j]), len([] if any([a,b])==0 else [(i,j) for i in range(a,b) for j in range(a+1,b+1) if i<j])

## TEST CASES ##
print ('test 1')
ans=combinations(1,7)
print (ans)
print ('test 2')
ans=combinations(3,5)
print (ans)
print ('test 3')
ans=combinations(-1,2)
print (ans)
print ('test 4')
ans=combinations(0,0)
print (ans)


# Q.6 [Total: 40 points]
# # Gaussian Elimination is an algorithm for solving systems of linear equations. In this question, you will write a
# # program to perform Gaussian Elimination. Note that the indices of the rows and columns for the matrices startfrom 0.
# # You will need to write and submit four functions:
# # a) readMatrix(f)
# # b) mulRowByC(matOp,i,c)
# # c) addRowMulByC(matOp,i,c,j)
# # d) gaussElimination(data)

# PART A ##
def readMatrix(f):
    list = []
    output = dict()
    for line in (f):
        if line == 'DATA\n':
            continue
        elif line == 'OP\n':
            output['matrix'] = list
            list = []
            continue
        elif line == 'END':
            break
        else:
            sublist = line.strip('\n')
            sublist = sublist.split(' ')
            sublist = [float(i) for i in sublist]
            list.append(sublist)
    output['op'] = list
    return output

## PART C ##
def addRowMulByC(matOp, i, c, j):
    m = [x*c for x in matOp[int(i)]]
    new = []
    for x in range(len(m)):
        new.append(m[x] + matOp[int(j)][x])
    matOp[int(j)] = new
    return matOp

## PART B ##
def mulRowByC(matOp, i, c):
    return (addRowMulByC(matOp, i, c-1, i))

## PART D ##
import copy
def gaussElimination(data):
    matA = copy.copy(data['matrix'])
    matOp = data['matrix']
    operations = data['op']
    for i in operations:
        if len(i) == 4:
            addRowMulByC(matOp, *(i[1:]))
        elif len(i) == 3:
            mulRowByC(matOp, *(i[1:]))
    return(matA, [[round(float(i), 2) for i in nested] for nested in matOp])

# OVERALL TEST CASES #
# replace filename with either 'gauss1.txt' or 'gauss2.txt'
f=open('/Users/[your name]/Desktop/midterm2016/midterm2016/gauss1.txt','r') #Change accordingly
data=readMatrix(f)
matA,result=gaussElimination(data)
print (matA)
print (result)
f.close()

#
# Q.7 [15 points]This question comprises of two parts: (a) Programming, and (b) Written.
# Write a function maxProductThree(num)that returns the maximum product from any three numbers in a list of integers.
# The list may contain both positive, zero, negative integers,and duplicates, but the maximum product can only be either
# a negative or a positive number. You can assume that the list contains at least three non-zero numbers so the maximum
# product will never be zero.
# Sample Solution 1: Brute Force (~30 seconds)

def maxProductThree(num):
 ls = []
 for i in num:
    for j in num:
       if j != i:
        for k in num:
          if k != j and k!= i:
            ls.append(i*j*k)
 return max(ls)

## TIMER ##
import time
start_time = time.time()
num=[6,-3,-10,0,2]
print (maxProductThree(num))
num = [4, -139, -848, -142, -779, 406, 899, -932, 565, -678, -197, 442, 145, -711, -495, -743, 602, 841, 312, 104, 814, 771, 772, 292, -718, -151, 503, -265, -812, 792, 209, -734, -343, -674, 129, -421, -138, 826, -739, 625, 529, 391, 367, -166, 764, -178, -104, -380, -204, 697, -471, -811, -580, 207, -980, -983, -478, -551, -591, 121, -51, -424, 922, -521, -789, 30, -121, 847, 862, -232, -669, 990, -878, 690, 408, -452, -713, -644, -163, 526, -929, 767, -174, 110, 477, 188, 575, 328, 685, 93, 596, -865, -987, -321, 28, 383, -53, 405, 271, 783, 25, -255, -885, 747, -375, -44, -515, 598, 100, 830, 915, -663, -32, 112, -686, -796, 647, -397, -592, -309, -6, -706, -47, 116, 528, 454, -201, -303, -927, -418, -177, -858, -294, -449, 738, 573, -143, -331, 392, 958, -964, 742, -472, -650, 725, 555, 34, -130, -769, 835, -659, 849, 500, -850, 933, -70, -504, -729, 366, -224, -531, 780, -974, 83, -373, 273, -956, 187, 106, 549, -961, 509, 837, 72, -785, -871, 821, -915, 676, 185, 261, -558, 692, 37, -474, -641, -498, 949, 873, 494, 582, -698, 239, -153, 186, 167, -169, 198, -754, 409, 431, 437, -4, 147, 804, 157, 35, 332, -78, 18, -483, -487, -813, 160, -210, -493, 396, 280, 97, -445, -649, -306, 56, 965, 305, 231, -690, -681, 163, 325, -862, 492, 15, -903, -685, 558, -968, 307, 768, -112, 179, 267, 781, -268, 576, -429, 63, -828, 832, -798, -85, -56, 171, 11, -579, -897, 663, -337, 463, 518, 6, -437, 820, 33, 330, -280, 745, -179, -654, -79, -301, -106, -628, 840, 486, 643, 535, 664, 438, 612, -363, -757, -503, -857, -843, 143, -661, 831, 38, -444, -494, 656, 661, -906, -803, 991, -451, 336, 283, -243, 258, 40, -863, -920, -295, 372, -621, 128, 897, -762, 253, 774, -550, 445, -349, -118, 760, -642, 534, -683, -338, -252, 809, 574, -966, -208, -392, -889, 58, 174, -619, -446, 300, 952, 434, -538, 469, -809, -205, -780, 932, 653, -72, 715, 50, -367, 220, -501, 333, -296, 892, -788, 196, 749, 375, 240, 581, -516, -396, -901, -473, -967, 560, -870, -537, 217, 646, 483, 401, 282, 592, -833, 590, -340, 813, -583, 740, -186, -45, -390, 470, -251, 127, -202, 317, 137, 998, 793, -466, 569, 732, 381, 491, 140, -573, -786, 269, 517, -119, 674]
print (maxProductThree(num))
print("--- %s seconds ---" % (time.time() - start_time))

# Sample Solution 2: Cases (~0.003 seconds)

def maxProductThree(num):
 i = [i for i in sorted(num) if i != 0]
 return max([i[-3]*i[-2]*i[-1],i[-1]*i[0]*i[1]])

## TIMER ##
import time
start_time = time.time()
num=[6,-3,-10,0,2]
print (maxProductThree(num))
num = [4, -139, -848, -142, -779, 406, 899, -932, 565, -678, -197, 442, 145, -711, -495, -743, 602, 841, 312, 104, 814, 771, 772, 292, -718, -151, 503, -265, -812, 792, 209, -734, -343, -674, 129, -421, -138, 826, -739, 625, 529, 391, 367, -166, 764, -178, -104, -380, -204, 697, -471, -811, -580, 207, -980, -983, -478, -551, -591, 121, -51, -424, 922, -521, -789, 30, -121, 847, 862, -232, -669, 990, -878, 690, 408, -452, -713, -644, -163, 526, -929, 767, -174, 110, 477, 188, 575, 328, 685, 93, 596, -865, -987, -321, 28, 383, -53, 405, 271, 783, 25, -255, -885, 747, -375, -44, -515, 598, 100, 830, 915, -663, -32, 112, -686, -796, 647, -397, -592, -309, -6, -706, -47, 116, 528, 454, -201, -303, -927, -418, -177, -858, -294, -449, 738, 573, -143, -331, 392, 958, -964, 742, -472, -650, 725, 555, 34, -130, -769, 835, -659, 849, 500, -850, 933, -70, -504, -729, 366, -224, -531, 780, -974, 83, -373, 273, -956, 187, 106, 549, -961, 509, 837, 72, -785, -871, 821, -915, 676, 185, 261, -558, 692, 37, -474, -641, -498, 949, 873, 494, 582, -698, 239, -153, 186, 167, -169, 198, -754, 409, 431, 437, -4, 147, 804, 157, 35, 332, -78, 18, -483, -487, -813, 160, -210, -493, 396, 280, 97, -445, -649, -306, 56, 965, 305, 231, -690, -681, 163, 325, -862, 492, 15, -903, -685, 558, -968, 307, 768, -112, 179, 267, 781, -268, 576, -429, 63, -828, 832, -798, -85, -56, 171, 11, -579, -897, 663, -337, 463, 518, 6, -437, 820, 33, 330, -280, 745, -179, -654, -79, -301, -106, -628, 840, 486, 643, 535, 664, 438, 612, -363, -757, -503, -857, -843, 143, -661, 831, 38, -444, -494, 656, 661, -906, -803, 991, -451, 336, 283, -243, 258, 40, -863, -920, -295, 372, -621, 128, 897, -762, 253, 774, -550, 445, -349, -118, 760, -642, 534, -683, -338, -252, 809, 574, -966, -208, -392, -889, 58, 174, -619, -446, 300, 952, 434, -538, 469, -809, -205, -780, 932, 653, -72, 715, 50, -367, 220, -501, 333, -296, 892, -788, 196, 749, 375, 240, 581, -516, -396, -901, -473, -967, 560, -870, -537, 217, 646, 483, 401, 282, 592, -833, 590, -340, 813, -583, 740, -186, -45, -390, 470, -251, 127, -202, 317, 137, 998, 793, -466, 569, 732, 381, 491, 140, -573, -786, 269, 517, -119, 674]
print (maxProductThree(num))
print("--- %s seconds ---" % (time.time() - start_time))

# b) There are only 2 potential cases: the product of the 3 largest intergers in the list or the product of the largest
# interger with the 2 smallest intergers from the list. The higher of the 2 cases yields the solution.


# DW midterm 2017 Solutions

import math
def combination(credit):
    credit = (credit - 10.0)/20.0 # 3
    out = 2.0*credit - 1.5
    return out
def activation(a):
    probability = 1.0/(1 + math.exp(-a))
    print(probability)
credit = 16.0
output = activation( combination(credit) ) # 14
print(credit)
print(output) # 16

# Q1(a) A student calculates that when line 3 is executed, credit will have a value of 0.3. However, when line 15 is
# executed, he is surprised to see 16.0 displayed on the screen. Explain why this is the case. (4 points)
# Sample answer 1:
# 'credit' in the function 'combination' refers to the function parameter credit from the local namespace, instead of
# the 'credit' from the global namespace. This is because the local namespace takes precedence over the global namespace.
# Hence 'credit' at line 3 is the assigned value resulting for the set of operations on the right. The change value
# of 'credit' from the local namespace does not affect 'credit' from the global namespace. Hence 'credit' in the global
# namespace remains as 16.0 as is printed as such.
#
# Sample answer 2:
# The student expects 0.3 to be displayed instead of 16.0 and is thus surprised. This suggests that the student expects
# the value of 'credit' from the local namespace of function 'combination' instead. However, since print(credit)is
# located at the outermost indentation level, 'credit' value of 16 from the global namespace is printed instead.
# To obtain the student's expected output of 0.3, the student should shift print(credit) to the line just after #3 at' \
#                      ' the same indentation level. This is because the local namespace takes precedence over the ' \
#                      'global namespace within the same indentation level of the function.
#
# Additional notes:
# We have seen that multiple namespaces can exist independently from each other and that they can contain the same
# variable names on different hierachy levels. The “scope” defines on which hierarchy level Python searches for a
#     particular “variable name” for its associated object. Now, the next question is: “In which order does Python
#     search the different levels of namespaces before it finds the name-to-object’ mapping?”
# To answer is: It uses the LEGB-rule, which stands for Local -> Enclosed -> Global -> Built-in, where the arrows
#     should denote the direction of the namespace-hierarchy search order.
# - Local can be inside a function or class method, for example.
# - Enclosed can be its enclosing function, e.g., if a function is wrapped inside another function.
# - Global refers to the uppermost level of the executing script itself, and
# - Built-in are special names that Python reserves for itself.
# So, if a particular name:object mapping cannot be found in the local namespaces, the namespaces of the enclosed
# scope are being searched next. If the search in the enclosed scope is unsuccessful, too, Python moves on to the
# global namespace, and eventually, it will search the built-in namespace (side note: if a name cannot found in
# any of the namespaces, a NameError will is raised).
#
# (b) When line 14 is executed, describe the sequence of the function calls and how data is passed among these
# functions and back to the variable output. Hence, explain why when line 16 is executed, None is displayed on
# the screen. (6 points)
# Sample answer:
# - activation is called, but it requires an argument a
# - combination is called, and passes credit as a argument
# - combination returns out, which is passed into activation
# - activation only prints probability but does not return anything
# - output is None, and is printed as such in line 16.
#
# Additional notes:
# A parameter is a variable in a method definition. When a method is called, the arguments are the data you
# pass into the method's parameters. Parameter is variable in the declaration of function.
# Argument is the actual value of this variable that gets passed to function.
#
# Q.2 [10 points]
# A student was asked to write a function to rotate the robot on itself a given number of times, either in clockwise
# or counterclockwise rotation. The student notes that it takes about 5 seconds to do a full rotation at 100% speed on
# one of the wheels. However, when testing his program, he realized something went wrong.
#
# A) Can you spot any syntax error in the code (including type errors) that would prevent the program to run?
# Please indicate the lines and how would you correct them. (3 points)
#
# B) After fixing syntax errors, can you spot any logical mistakes in the code? Please indicate the lines and how
# would you correct them. Briefly summarize the behavior of the robot for the given test case if the logical mistakes
# are not fixed. (7 points)

from eBot import eBot
# import import time # SYNTHAX ERROR: double import
import time


# Rotate and conquer
def one_round(direction):  # LOGICAL ERROR: direction not speed
    if direction == 'clockwise':  # SYNTAX ERROR: == instead of =
        ebot.wheels(0, 1)
    elif direction == 'counter clockwise':  # SYNTAX ERROR: == instead of =
        ebot.wheels(1, 0)
    time.sleep(2.5)


def rotate(times, direction):
    # print ('Rotating' + times + ' times in direction: ' + direction) # SYNTHAX ERROR: int + str type
    print('Rotating' + str(times) + ' times in direction: ' + str(direction))
    for i in range(1, times):
        one_round(direction)  # LOGICAL ERROR: direction not speed
        time.sleep(3)  # LOGICAL ERROR: delay for loop before next iteration


ebot = eBot.eBot()  # create an eBot object
ebot.connect()  # connect to the eBot via Bluetooth
# Test case, this should rotate 3 full times clockwise...
rotate(3, clockwise)
# Prof! My code does not work!

ebot.disconnect()  # disconnect the Bluetooth communication

# The robot will only rotate clockwise once.¶
# Q.3 [5 points]
# A regular polygon is an n-sided polygon in which all sides are of the same length and all angles have the same degree.
# The formula for computing the area of a regular polygon is: $$ Area = \frac{n\times s^{2}}{4\times tan(\frac{n}{\pi})} $$
# Here, s is the length of a side. Write a function named area_r_polygon that takes the number of sides and the length
# of a side as arguments, then returns the area of the regular polygon up to 3 decimal places.
# Note: Use math.pi to obtain an accurate value for pi.

import math
def area_r_polygon(n,s):
    return round(n*s**2/(4*math.tan(math.pi/n)),3)
# area_r_polygon = lambda n,s: (round(n*s**2/(4*math.tan(math.pi/n)),3))

## TEST CASES ##
print('Test case 1: n=5, s=6.5')
print(area_r_polygon(5, 6.5))
print('Test case 2: n=7, s=3.25')
print(area_r_polygon(7, 3.25))
print('Test case 3: n=2, s=12.5')
print(area_r_polygon(2, 12.5))


# Q.4 [10 points]
# The number 3, 5, 6, and 9 are all integers below 10 that are multiples of either 3 or 5; the sum of 3, 5, 6, 9 is 23.
# Similarly, 2, 4, 6, 8, 10 are all integers below 12 that are multiples of either 2 or 4; the sum of 2,4, 6, 8, 10 is 30.
#
# Write a function mysum(a,b,limit) that accepts three arguments: a, b and limit. The arguments a and b are integers
# greater than zero and lesser than limit. The function mysum(a,b,limit) should return the sum of all the multiples of
# a or b, the multiples being lesser than limit. If the user enters a or b to be less than zeroor non-integers, the
# function should return the error message “Wrong input” as a string.


def mysum(a,b,limit):
    try:
        if a >= limit or b >= limit or a <= 0 or b <= 0:
            return 'Wrong input'
        if type(a) != int or type(b) != int:
            return 'Wrong input'
    except:
        return 'Wrong input'
    sum_ = 0
    for i in range(int(limit/a)+1):
        if i*a < limit:
            sum_ += i*a

    for j in range(int(limit/b)+1):
        if j*b < limit:
            sum_ += j*b

    for k in range(int(limit/(a*b))+1):
        if k*a*b < limit:
            sum_ -= k*a*b

    return sum_

# TEST CASES #
print(mysum(3,5,10))
print(mysum(2,4,12))
print(mysum(3,3,15))
print(mysum(7,9,100))
print(mysum(21,34,10000))
print(mysum(0,5,10))
print(mysum(0.5,5,10))
print(mysum(3,'x',10))
print(mysum(2,3,0)) # Returns 'Wrong input' as a and b should be lesser than limit

# Q.5 [10points]
# Write a function called get_students(students, course)which takes in a list and a string. The input list ismade up of
# a sequence of binary tuples, each with a student name and a list of courses that the student has enrolled in.
# The second argument is astring containing the name of a course. Your function should return a list of the names
# of students who are enrolled in that course. If no students are taking the course the function should return an empty list.

students = [("Alan", ["CompSci", "Physics", "Math"]),
            ("Justin", ["Math", "CompSci", "Stats"]),
            ("Edward", ["CompSci", "Philosophy", "Economics"]),
            ("Margaret", ["InfSys", "Accounting", "Economics", "CommLaw"]),
            ("Philip", ["Sociology", "Economics", "Law", "Stats", "Music"]),
            ("Mary", ["Math", "CompSci", "Stats"]),
            ("Vera", ["CompSci", "Philosophy", "Economics"]),
            ("Mike", ["InfSys", "Accounting", "Economics", "CommLaw"]),
            ("Donna", ["Sociology", "Economics", "Law", "Stats"])]

def get_students(students, subject):
    lst = []
    for i in range(len(students)):
        if subject in students[i][1]:
            lst.append(students[i][0])
    return lst

## TEST CASES ##
print(get_students(students, 'Philosophy'))
print(get_students(students, 'History'))
print(get_students(students, 'Math'))
print(get_students(students, 'CompSci'))


# Q.6 [Total: 30 points]
# SUTDBook is a social network website founded by one of SUTD graduates. They are currently hiring some software
# engineers to develop an algorithm to suggest new ‘friends’ to their user. Your task in this question is to build
# this new friends suggestion algorithm.
#
# Network of users can be represented as a graph of connected nodes where each user is a node. The connection between
# two nodes statesa friend relationship between the two users.
#
# a) [5points]
# Write a function called get_nodes(fid) which takes in a file object as its input arguments and outputs a list
# of tuples. Each tuple shows a friend connection between two users and each user is represented by an integer.
#
# b) [10 points]
# Write a function create_graph(nodes) which takes in a list of tuples and returns a graph of friend connection as
# a dictionary. The list of tuples is obtained from the output of get_nodes(fid)in part (a) and represents a friend
# connection between two users. The outputof the function is a dictionary with each user as a key. The value is also
# a dictionarythat contains key-value pair of each of the user’s friends.
#
# c) [5points]
# Write a functioncalled get_friends(G,node) that takes in two arguments. The first argument is a dictionary that
# contains the network of friends and the second argument is the node(or user)of interest.
# The function get_friends(G,node)returns a list of friends that particular user has.
#
# d) [10 points]
# Write a function called suggested_new_friends(G,node) that takes in a dictionary describing the friends network and
# an integer representing a user. The function returns a list of suggested new friends for the input user.


# PART A ##
def get_nodes(fid):
    lines = fid.readlines()
    lines_s = [line.strip("\n") for line in lines]
    lines_r = [line.strip("\r") for line in lines_s]
    tuples_ = [line.split(" ") for line in lines_r]
    tuples_i = [tuple([int(number) for number in pair]) for pair in tuples_]
    return tuples_i

## PART B ##
def create_graph(nodes):
    g ={}
    for a, b in nodes:
     g.setdefault(a, {}).update({b:1})
     a,b = b,a
     g.setdefault(a, {}).update({b:1})
    return g

## PART C ##
def get_friends(G,user):
    return list(G[user])
#get_friends = lambda G,user: list(G[user])

## PART D ##
def most_frequent(lst):
    count_dict = {}
    for i in lst:
        if count_dict.get(i) == None:
         count_dict.update({i : lst.count(i)})
    return [x for x in count_dict.keys() if count_dict[x] == max(count_dict.values())], max(count_dict.values())

def suggested_new_friends(G,node):
    friends_list = get_friends(G,node)
    friend_friend_list = []
    for friend_friend in friends_list:
        for friend_friend in get_friends(G,friend_friend):
            if friend_friend != node:
                friend_friend_list.append(friend_friend)
    return most_frequent(friend_friend_list)

# OVERALL TEST CASES #
f=open('/Users/[yourname]/Desktop/midterm2017/midterm2017/facebook_less.txt','r') #Change accordingly
nodes= get_nodes(f)
G= create_graph(nodes)
print ('Friends of 1from facebook_less.txt')
print (get_friends(G,1))
print ('Suggested new friends for 1')
print (suggested_new_friends(G,1))
f.close()
f=open('/Users/[yourname]/Desktop/midterm2017/midterm2017/sutdbook1.txt','r') #Change accordingly
nodes= get_nodes(f)
G= create_graph(nodes)
print ('Friends of 0 from sutdbook1.txt')
print (get_friends(G,0))
print ('Suggested new friends for 0')
print (suggested_new_friends(G,0))
f.close()
f=open('/Users/[yourname]/Desktop/midterm2017/midterm2017/sutdbook2.txt','r') #Change accordingly
nodes= get_nodes(f)
G= create_graph(nodes)
print ('Friends of 0 from sutdbook2.txt')
print (get_friends(G,0))
print ('Suggested new friends for 0')
print (suggested_new_friends(G,0))
f.close()

# Q.7 [15 points]
# Design an efficient algorithm that determines the number of ways 5 non-negative integers can sum up to N.
# Your code must be able to handle large N values (150+) under 20 seconds. (Timer included to each cell as proof)
# Negative Example: Brute Force (Don't even try to time it)

def nos(n):
 ans = 0
 for i in range(n+1):
    for j in range(n+1):
        for k in range(n+1):
            for l in range(n+1):
                for m in range(n+1):
                    if i + j + k + l + m == n:
                        ans += 1
 return ans


# Sample Solution 1: Recursion + Nested Loops (~10 seconds)

def nos(n, x = 5):
    if x == 1:
        return 1
    if n == 0:
        return 1
    else:
        solutions = 0
        for i in range(x):
            for j in range(n):
                solutions += nos(j, i)
        return solutions
## TIMER ##
import time
start_time = time.time()
print(nos(150,5))
print("--- %s seconds ---" % (time.time() - start_time))

# Sample Solution 2: Pure Recursion (~4 seconds)

def nos(n, x=5):
    if x == 1:
        return 1
    if n == 1:
        return x
    if n == 0:
        return 1
    return sum([nos(n-m,x-1) for m in range(n+1)])
## TIMER ##
import time
start_time = time.time()
print(nos(5,5))
print("--- %s seconds ---" % (time.time() - start_time))


# Sample Solution 3: Top-down DP with Memomisation & Decorators (Sample 1: ~0.1 seconds; Sample 2: ~0.04 seconds)


def memo_nos(f):
    nos_ans = {}
    def helper_nos(*args):
        if str(args) not in nos_ans:
            nos_ans[str(args)] = f(args[0],args[1])
        return nos_ans[str(args)]
    return helper_nos
@memo_nos

# COPY PASTE EITHER SAMPLE 1 OR SAMPLE 2 CODE HERE

## TIMER ##
import time
start_time = time.time()
print(nos(150,5))
print("--- %s seconds ---" % (time.time() - start_time))

# Sample Solution 4: Combinatorics (<0.001 seconds; so fast that Python might return 0 seconds)

from scipy.special import comb
def nos(n, x=5):
    return int(comb(n+x-1 , x-1, exact=False, repetition=False))
#nos = lambda n, x=5: comb(n+x-1 , x-1, exact=False, repetition=False)
## TIMER ##
import time
start_time = time.time()
print(nos(150,5))
print("--- %s seconds ---" % (time.time() - start_time))

# Sample Solution 5: Combinatorics with loops (~1 second; Uses the same logic as solution 4)¶

def nos(n ,x=5):
    sol = 0
    for a in range(n + 1):
        for b in range(n + 1 - a):
            for c in range(n + 1 - a - b):
                for d in range(n + 1 - a - b - c):
                    sol += 1
    return sol
## TIMER ##
import time
start_time = time.time()
print(nos(150,5))
print("--- %s seconds ---" % (time.time() - start_time))

# DW mid-terms 2018 solutions
# These solutions are student compiled and might contain errors (especially for qn 1 & 2)
# Credit goes to Team Anonymous on Piazza
#
# Part A


x = 'aces'
y = 1.23
z = x
x = y
y = z
print(x, y)

# Q1(a) After the above code is executed, at line 6, what is seen on the screen is 1.23aces.
#
# By explaining what each line of the code below (for lines 1 to 5) does, show how the code below switches the objects
# assigned to variables x and y.
#
# Your explanation must state when objects are created and their data types, and also how names are assigned to these
# objects. Diagrams could be helpful in your explanation. (6 points)
# Sample answer 1:
# At line 1, string object 'aces' is assigned to frame 'x'. At line 2, float object of value 1.23 is assigned to frame
# 'y'. At line 3, a shallow copy of string object 'aces' from frame 'x' is assigned to frame 'z'. At line 4, a shallow
# copy of the float object of value 1.23 from frame 'y' is assigned to frame 'x' and overwirtes the string object 'aces'.
# At line 5, a shallow copy of string object 'aces' from frame 'z' is assigned to frame 'y' and overwrites the float
# object of value 1.23. The final result is:
# Stack | Object after line 3 > 4 > 5
# x | 'aces' > 1.23 > 1.23
# y | 1.23 > 1.23 > 'aces'
# z | 'aces' > 'aces' > 'aces'
# The contents of both x & y are fetched when print(x,y) is executed at line 6. Thus, 1.23aces is printed
# in the order of x then y.
#
# Q1(b) When the following code is executed, after line 12, what is seen on the screen is True.
#
# i) Using how names are assigned to objects in memory, explain why. Diagrams could be helpful in your explanation.
# (3 points)
#
# ii) The intention of the programmer is to create two lists showing the words for ‘seven’, ‘eight’ and ‘nine’ for both
# Greek and French. State one modification to the code at line 8 so that line 13 prints out the correct output. (1point)

french= [ 'sept', 'huit', 'neuf'] # the words mean 'seven','eight','nine'.
greek = french # this is line 8
greek[0] = 'epta' # 'seven'
greek[1] = 'okto' # 'eight'
greek[2] = 'enea' # 'nine'
print(greek is french) # this is 'line 12'
print(french, greek)

# Sample answer 1:
# i) At line 1, a list object is created for frame 'french'. At line 2, frame 'greek' is assigned to refer to the same
# list object as frame 'french'. Thus, even after the contents of the list is changed, line 12's shallow equivalence
# check 'is' returns true as both frames 'french' and 'greek' still refers to the same list object in memory.
#
# ii) I would repace line 8 with 'greek = list(french)' to another list object similar in contents to frame 'french'
# is created for frame 'greek'.
#
# Q2 [10 points]
# a) Is there anything wrong with the following program? If yes, what is wrong? (2 points)

import = int(input("Enter a number: "))
if import==2:
    print("Yes")
else:
    print("No")

# Sample solution 1:
# Synthax error. 'import' is a reserved keyword in python and neither be used as a variable name nor be assigned to
# an 'int' type object.
#
# b) If break is removed from the following programand the program is run, what will be printed out? (2 points)

my_string = "Computing"
for character in my_string:
    print(character)
    if character == "u":
        print("Found 'u' :)")

# Solution:
# C
# o
# m
# p
# u
# Found 'u' :)
# t
# i
# n
# g
#
# c) Look at the following function:

def my_function(n):
    return_value = None
    if n == 0 or n == 1:
        return_value = False # not run
    i=2
    while i*0.5:
        if n%i==0:
            return_value = False # not run
            break # not run
        i += 1
        return_value = True
    return return_value
my_function(37)


# Let the function tested to be my_function(37)
#
# a) What will be the output? (1 point)
#
# b) Identify the lines of the program which will be executed when the input is 37.
# Do this by entering the codes from those lines to eDimension. (2 points)
# Solution:
# a) True.
# b) (enter those lines of code not tagged by # not run)
#
# d) In the context of the 1D projects you have completed so far in this course, look at the following function:

def forward(speed, duration):
    robot.wheels(speed, speed)
    robot.sleep(duration)
    robot.wheels(0,0)

# a) Why is it not necessary to have a return statement in this function? (1 point)
#
# b) If we change robot.wheels(speed, speed) to robot.wheels(speed1, speed2) and the function header is also modified
# to take in speed1 and speed2, how will the movement of the robot change? You can assume that speed1 and speed2 are
# different and both speed1 and speed2 are positive numbers. (1 point)
# Sample solution 1:
# a) In the above function to move the robot forward at some speed for some duration, no values are expected to be
# reused or caught by the function. Hence, no return statement is required.
#
# b) If speed1 > speed2, the robot will still travel forward but with a leftwards dispalcement. If speed1 < speed2,
# the robot will still travel forward but with a rightwards dispalcement.
#
# Part B
#
# Q3 [10 points]
#
# A frustum is a parallel truncation of a right pyramid. A piece of metal is in the shape of a frustum with a square
# base. The side length of the top square is s1 and the side length of the bottom square is s2.
# The height of the frustum is H.
#
# The volume of the frustum is given by the formula:
# $$Volume = \frac{H}{3}(A1 + A2 + \sqrt{A1 \text{ x } A2})$$ where A1 is the area of the upper square, A2 is the
# area of the lower square, and H is the height of the frustum.
# a) Write a python function area_square(s) that takes the side of a square as an input argument s, and returns the
# area of the square.
#
# b) Write a python function vol_frustum(top_area, bottom_area, height) that takes three arguments, a top area,
# a bottom area and a height in that order, and returns the volume of the frustum.
#
# c) Write a python function get_volume(s1, s2, height) that takes three arguments, a top side length, a bottom
# side length and a height and returns the volume of a frustum based on those dimensions.
# This function should first call area_square to obtain the two needed areas, and then call vol_frustum to evaluate
# the volume.
#
# All input arguments and return values are floats. Please round only your final output of get_volume to three
# decimal places. Please use math.sqrt() to compute the square root in your python code. Note that you get only
# full marks if your get_volume function makes use of the other two functions.

import math


def area_square(s):
    return s ** 2.


def vol_frustum(top_area, bottom_area, height):
    return (height / 3) * (top_area + bottom_area + math.sqrt(top_area * bottom_area))


def get_volume(s1, s2, height):
    return round(vol_frustum(area_square(s1), area_square(s2), height), 3)


## TEST CASES ##
print('{:.3f}'.format(area_square(2)))
print('{:.3f}'.format(area_square(3)))
print('{:.3f}'.format(vol_frustum(1, 4, 2)))
print('{:.3f}'.format(vol_frustum(2, 2, 2)))
print('{:.3f}'.format(get_volume(1, 2, 2)))
print('{:.3f}'.format(get_volume(1.5, 3.3, 5.0)))
print('{:.3f}'.format(get_volume(3.6, 6.4, 4.0)))


# Q4 [10 points]
#
# Implement a function determinant(matrix) that takes a matrix as input (represented as a nested list) and returns
# its determinant as output. The function should satisfy the following requirements:
#
# 1) If the input matrixis not of dimension n x n (for 1 ≤n ≤3), the function should return None
#
# 2) The function is to be implemented withoutimporting any libraries.

def determinant(matrix):
    M = matrix
    try:
        a = len(M)
        a = len(M[0])
    except:
        return None

    if len(M) == 1:
        for row in M:
            if len(row) != 1:
                return None
        return M[0][0]
    if len(M) == 2:
        for row in M:
            if len(row) != 2:
                return None
        return M[0][0] * M[1][1] - M[0][1] * M[1][0]
    if len(M) == 3:
        for row in M:
            if len(row) != 3:
                return None
        return (M[0][0] * M[1][1] * M[2][2]
                + M[1][0] * M[2][1] * M[0][2]
                + M[2][0] * M[0][1] * M[1][2]
                - M[0][2] * M[1][1] * M[2][0]
                - M[1][0] * M[0][1] * M[2][2]
                - M[0][0] * M[2][1] * M[1][2])


## TEST CASES ##
print(determinant([[100]]))
print(determinant([[-5, -4], [-2, -3]]))
print(determinant([[2, -3, 1], [2, 0, -1], [1, 4, 5]]))
print(determinant([[0, 3, 5], [5, 5, 2], [3, 4, 3]]))
print(determinant([[23], [-4, 4]]))

# Q5 [15 points]
#
# The Newton-Raphson (NR) method is an iterative method that approximates the root of a function.
# The accuracy of the answer is enhanced in successive iterations.
#
# You need two create two functions: nrootand nroot_complex. The function nroot(n, i, num) is to determine
# the root of non-negative num. The function nroot_complex(n,i,num) to determine the root of negative num.
# Thefunction nroot_complex should call nroot to do the NR approximation. Note the output should give a constant$*$1j
# where j is the imaginary square root of -1. For odd n the output should give a negative value instead of constant$*$1j.
# This means that:
# • When num is a non-negative number, nroot_complex should give the same result as nroot.
# • When num is a negative number and n is even, nroot_complex should give a complex number with no real part,
# and its magnitude is the same as the output of nrootwhen num is positive.
# • When numis a negative number and n is odd, nroot_complex should give a negative real number, and its magnitude is
# the same as the output of nroot when numis positive.
#
# Round the output of nroot to 3 decimal places.Use x = 1 as your initial value.

def nroot(n, t, num):
    x = 1
    for i in range(t):
        x -= ((x ** n - num) / (n * (x ** (n - 1))))
    return round(x, 3)


def nroot_complex(n, t, num):
    if num > 0 or num == 0:
        return nroot(n, t, num)
    if num < 0 and n % 2 == 1:
        return -nroot(n, t, -num)
    else:
        return str(nroot(n, t, -num)) + 'j'


## TEST CASES ##
print(nroot(2, 5, 2))
print(nroot_complex(2, 5, -4))
print(nroot_complex(3, 5, -8))

# Q6 [30 points]
#
# In this problem you will write a program to find a path through MRT stations from a starting station to
# an ending station with a maximum of one interchange.
#
# The overall function is called find_path and it takes three arguments: (1) a file object to a text file
# containing the MRT lines and stations, (2) the starting station, and (3) the ending station.
#
# This function should return a list of stations from a starting station to an ending station with a maximum
# of one interchange. The problem is decomposed by writing several other functions, described in the following parts.
#
# For simplicity, the information given to you in this question is limited to the North South line and the East West line.
# Also, the branch line to Changi Airport is treated as a separate line. Hence the three lines are labelled
# in this question as follows: (1) NorthSouthLine (2) EastWestLine (EW) and (3) EastWestLine (CG).
#
# a) read_stations(f): This function takes in a file object and returnsa dictionary. The dictionary has the MRT lines
# as its keys. The value of each key is a list of stations in that MRT line.
#
# b) get_stationline(mrt): This function takes in a dictionary of MRT lines (i.e. the output of part (a) ).
# The function returns another dictionary which contains all the stations as its keys. The value for each key is a list
# of the MRT lines for that particular station. Note that if the station is an interchange, the list should contain all
# the lines calling at that station.
#
# c) get_interchange(stationline): This function takes in a dictionary of stations and their lines
# (i.e. the output of part (b)). The function returns another dictionary which contains all the interchange stations as
# its keys. The value for each key is a list of the MRT lines for that particular interchange stations.
#
# d) find_path(f, start, end): This function takes in three arguments: (1) the file object to a text file
# containing all the MRT lines and stations, (2) the startingstation, and (3) the endingstation. The function should
# return a list of stations starting from the starting station to the ending station with a maximum of one interchange.
# If there are more than one possible paths, the following considerations should be taken into account:
#
# • If there is a path without changing MRT lines, the result should return this path.
# • If the path must involve changing MRT lines, it will return the path with a minimum number of stations and
# containing only one interchange station.
# • If no such path can be found as above, it will return None.

## PART A ##
def read_stations(s):
    st = ''.join(s.readlines()).split("=")
    ans = {}
    for i in range(int((len(st) - 1) / 2)):
        ans[st[2 * i + 1]] = (st[2 * (i + 1)].strip("\n")).split(", ")
    return ans


## PART B ##
def get_stationline(mrt):
    ans = {}
    for lines in mrt:
        for station in mrt[lines]:
            if station not in ans:
                ans[station] = []
                ans[station].append(lines)
            else:
                ans[station].append(lines)
    return ans


## PART C ##
def get_interchange(stationline):
    ans = {}
    for stations in stationline:
        if len(stationline[stations]) > 1:
            ans[stations] = stationline[stations]
    return ans


## PART D ##
def create_graph(f):
    stations = read_stations(f)
    stationline = get_stationline(stations)
    interchange = [*get_interchange(stationline)]
    network = {}
    # Create a dictionary where station (key) is linked to all its connected stations (values)
    for line in stations:
        stations_ = stations.get(line)
        network[stations_[-1]] = [stations_[-2]]  # End stations only conencted to one other station
        for station in range(0, len(stations_) - 1):
            network.setdefault(stations_[station], [stations_[station - 1]]).append(stations_[station + 1])
        if stations_[0] not in interchange:
            network[stations_[0]] = [stations_[1]]  # Removing non interchange connections that loops
    network['City Hall'].append('Dhoby Ghaut')  # Add this pesky back edge
    for key in network:
        network[key] = set(network.get(key))  # Apply set structure to dictionary values
    return network


def bfs_paths(graph, start, goal):
    # Since we only want the shortest path, we use Breath First Search on a queue structure for efficiency
    queue = [(start, [start])]
    while queue:
        (vertex, path) = queue.pop(0)
        for next in graph[vertex] - set(path):
            if next == goal:
                yield path + [next]
            else:
                queue.append((next, path + [next]))


import collections as c


def find_path(f, start, end):
    try:
        graph = create_graph(f)
        possible_paths = list(bfs_paths(graph, start, end))
        f.seek(0)  # remember to reset readlines() counter to 0
        stations = read_stations(f)
        stationline = get_stationline(stations)
        interchange = [*get_interchange(stationline)]
        ans = []
        for path in possible_paths:
            line_counter = []
            for station in path:
                line_counter.append(stationline.get(station)[0])
            # We count the total number of line types present. More than 1 interchange used if line type > 2
            if len(c.Counter(line_counter)) <= 2:
                ans.append(path)
        return ans[0]  # Since we used BFS, first path found is always the shortest
    except:
        # A general catch block to return None
        return None

    ## TEST CASES ##


print('Test 1')
f = open('mrt_lines_short.txt', 'r')  # Make sure directory is correct
ans = find_path(f, 'Boon Lay', 'Clementi')
print(ans)
f.close()
print('Test 2')
f = open('mrt_lines_short.txt', 'r')  # Make sure directory is correct
ans = find_path(f, 'Changi Airport', 'Orchard')
print(ans)
f.close()
print('Test 3')
f = open('mrt_lines_short.txt', 'r')  # Make sure directory is correct
ans = find_path(f, 'Boon Lay', 'Bukit Gombak')
print(ans)
f.close()
print('Test 4')
f = open('mrt_lines_short.txt', 'r')  # Make sure directory is correct
ans = find_path(f, 'Tanah Merah', 'Orchard')
print(ans)
f.close()


# Q7 [15 points]
#
# Write a function decompose(pence), that takes as input some number of pence (as an integer), and returns as output
# an integer expressing how many different ways that the amount can be made up by using the available coins.
# At present, there are eight coins in general circulation:
#
# 1p, 2p, 5p, 10p, 20p, 50p, £1, and £2
#
# Note that the function decompose(pence) can be implemented in a number of different ways,
# including by using brute force (i.e. exhaustive search). However, brute force implementations may only score a
# maximum of 12 points; the full 15 are only available for more elegant/efficient solutions.
# Sample Solution 1: Naive for loops (Brute force; Don't even try to time it)

def decompose(pence):
    coins = [1, 2, 5, 10, 20, 50, 100, 200]
    count = 0
    for x1 in range(pence):
        for x2 in range(pence):
            for x3 in range(pence):
                for x4 in range(pence):
                    for x5 in range(pence):
                        for x6 in range(pence):
                            for x7 in range(pence):
                                for x8 in range(pence):
                                    if (x1 * coins[0] + x2 * coins[1] + x3 * coins[2] + x4 * coins[3]
                                            + x5 * coins[4] + x6 * coins[5] + x7 * coins[6] + x8 * coins[7])
                                        == pence:
                                        count += 1  # Cancer


return count + 1

## TEST CASES ##
import time

start_time = time.time()
print(decompose(1))
print(decompose(5))
print(decompose(7))
print(decompose(130))
print(decompose(200))
print(decompose(700))
print("--- %s seconds ---" % (time.time() - start_time))


# Sample Solution 2: Pure recursion (Exhasutive; ~10mins)

def decompose(pence, num_types=len(coins)):
    coins = [1, 2, 5, 10, 20, 50, 100, 200]
    # If pence = 0 then there is only 1 solution
    if (pence == 0):
        return 1

    # If n is less than 0 then no solution exists
    if (pence < 0):
        return 0;

        # If there are no coins and n is greater than 0, then no solution exist
    if (num_types <= 0 and pence >= 1):
        return 0

    # Recursion step
    return decompose(pence, num_types - 1) + decompose(pence - coins[num_types - 1], num_types);


## TEST CASES ##
import time

start_time = time.time()
print(decompose(1))
print(decompose(5))
print(decompose(7))
print(decompose(130))
print(decompose(200))
print(decompose(700))
print("--- %s seconds ---" % (time.time() - start_time))

# Sample Solution 3: Recursion with some memomisation (Exhasutive with some elegance; ~5mins)

def decompose(pence, coins=[1, 2, 5, 10, 20, 50, 100, 200]):
    # If pence = 0 then there is only 1 solution
    if pence == 0:
        return 1

    # If n is less than 0 then no solution exists
    if pence < 0:
        return 0
    num_ways = 0

    # Store previously computed sub-problems in a dictionary to avoid re-computing it
    dic_ways = {}
    for i in range(len(coins)):
        coin = coins[i]
        if pence - coin not in dic_ways:
            # Recursion step
            num_ways += decompose(pence - coin, coins[i:])
            dic_ways[pence - coin] = True
    return num_ways


## TEST CASES ##
import time

start_time = time.time()
print(decompose(1))
print(decompose(5))
print(decompose(7))
print(decompose(130))
print(decompose(200))
print(decompose(700))
print("--- %s seconds ---" % (time.time() - start_time))

# Sample solution 4: Expansion of partition equation into series (Elegant but inefficient; ~30s)

from sympy import *
def decompose(pence):
    x = symbols('x')
    partition_series = series(1/(( 1 - x)*(1-x**2)*(1-x**5)*(1-x**10)
    *(1-x**20)*(1-x**50)*(1-x**100)*(1-x**200)), n = pence+2)
    coef = Poly(partition_series.removeO(),x)
    return coef.all_coeffs()[1]

## TEST CASES ##
import time
start_time = time.time()
print (decompose(1))
print (decompose(5))
print (decompose(7))
print (decompose(130))
print (decompose(200))
print (decompose(700))
print("--- %s seconds ---" % (time.time() - start_time))

# Sample solution 5: Smart brute force (Efficent but inelegant; 1.5s)

def decompose(pence):
    count = 0
    for x1 in range(0,pence+1,200):
        for x2 in range(x1,pence+1,100):
            for x3 in range(x2,pence+1,50):
                for x4 in range(x3,pence+1,20):
                    for x5 in range(x4,pence+1,10):
                        for x6 in range(x5,pence+1,5):
                            for x7 in range(x6,pence+1,2):
                                count+=1
    return count

## TEST CASES ##
import time
start_time = time.time()
print (decompose(1))
print (decompose(5))
print (decompose(7))
print (decompose(130))
print (decompose(200))
print (decompose(700))
print("--- %s seconds ---" % (time.time() - start_time))

# Sample solution 6: Top-down Dynamic Programming; Recursion with memoization & decorators
# (Efficient & quite elegant; ~0.002s)

def memoize(func):
    cache = dict()

    def memoized_func(*args):
        if args in cache:
            return cache[args]
        result = func(*args)
        cache[args] = result
        return result

    return memoized_func


## The above code is a quick and dirty memo fucntion that can be used widely ##
## to speed up problems with overlapping sub-problems (avoid recomputation) ##

@memoize
## Pure recursion from sample solution 2 ##
def decompose(pence, num_types=len(coins)):
    coins = [1, 2, 5, 10, 20, 50, 100, 200]
    # If pence = 0 then there is only 1 solution
    if (pence == 0):
        return 1

    # If n is less than 0 then no solution exists
    if (pence < 0):
        return 0;

        # If there are no coins and n is greater than 0, then no solution exist
    if (num_types <= 0 and pence >= 1):
        return 0

    # Recursion step
    return decompose(pence, num_types - 1) + decompose(pence - coins[num_types - 1], num_types);


## TEST CASES ##
import time

start_time = time.time()
print(decompose(1))
print(decompose(5))
print(decompose(7))
print(decompose(130))
print(decompose(200))
print(decompose(700))
print("--- %s seconds ---" % (time.time() - start_time))

# Sample Solution 7: Bottom-up Dynamic Programming (Most elegant & efficient; ~0.001s)

def decompose(pence):
    try:
        coins = [1, 2, 5, 10, 20, 50, 100, 200]
        num_types = len(coins)
        # table[i] will be storing the number of solutions for
        # value i. We need n+1 rows as the table is constructed
        # in bottom up manner using the base case (pence = 0)
        # We first initialize all table values as 0
        table = [0 for accumulative_num_of_ways in range(pence + 1)]

        # If pence = 0 then there is only 1 solution
        table[0] = 1

        # Pick all coins one by one and update the table[] values
        # after the index greater than or equal to the value of the
        # picked coin
        for type_ in range(0, num_types):
            for value in range(coins[type_], pence + 1):
                table[value] += table[value - coins[type_]]
                # We only want the number of ways to find change for value = pence
        return table[pence]
    except:
        # Our bottom up approach innately deals with special cases
        # this line catches invalid arguments
        return 0

    ## TEST CASES ##


import time

start_time = time.time()
print(decompose(1))
print(decompose(5))
print(decompose(7))
print(decompose(130))
print(decompose(200))
print(decompose(700))
print("--- %s seconds ---" % (time.time() - start_time))

Advertisements
Loading...

We use cookies to provide and improve our services. By using our site, you consent to our Cookies Policy.