def greatestDifference(inputList): minItem = min(inputList) maxItem = max(inputList) print("{} and {} have the greatest difference: {}".format(minItem, maxItem, maxItem - minItem)) def smallestDifference(inputList): sortedList = sorted(inputList) minDiffSoFar = sortedList[1] - sortedList[0] minIndex = 0 for index in range(len(sortedList)-1): newDiff = sortedList[index+1] - sortedList[index] if newDiff < minDiffSoFar: minDiffSoFar = newDiff minIndex = index print("{} and {} have the smallest difference: {}".format(sortedList[minIndex], sortedList[minIndex+1], minDiffSoFar)) # This method is not quite correct since it allows same item to be used twice: # E.g. for inputList == [1, 2, 5] and k == 4, it would yield "pair" 2, 2 def findKPairSlow1(inputList, k): for item1 in inputList: for item2 in inputList: if (item1 + item2 == k): print("Yay! Found a pair ({}, {}) that sum to {}".format(item1,item2, k)) return (item1, item2) print("No, I couldn't find a pair for the given target.") # A correct slow method. Does not allow same item to be used twice (unless the number # appears more than once in the input list). # Thus, fails on input: [1, 2, 5], 4 # and succeeds on : [1, 2, 5, 2], 4 # def findKPairSlow(inputList, k): for index1 in range(len(inputList)): item1 = inputList[index1] for index2 in range(index1+1, len(inputList)): item2 = inputList[index2] if (item1 + item2 == k): print("Yay! Found a pair ({}, {}) at indices {} and {} that sum to {}".format(item1,item2, index1, index2 , k)) return (item1, item2) print("No, I couldn't find a pair for the given target.") # Like findKPairSlow1, this version is not *quite* correct since it allows same item # to be used twice even if it appears only once it list. # This can be fixed without changing speed but providing this version # first since it clearly conveys the basic idea. # (see findKPairFastCorrectReturn if interested in fully correct version) # def findKPairFast1(inputList, k): mydict = {} for item in inputList: mydict[item] = True for item in inputList: if (k-item) in mydict: print("Yay! Found a pair ({:d}, {:d}) that sum to {:d}".format(item,k-item, k)) return print("No, I couldn't find a pair for the given target.") # same as findKPairFast1 but provides location of pair in original list # def findKPairFast1Plus(inputList, k): mydict = {} for index in range(len(inputList)): item = inputList[index] mydict[item] = index for index in range(len(inputList)): item = inputList[index] if (k-item) in mydict: print("Yay! Found a pair ({:d}, {:d}) at indices {} and {} that sum to {:d}".format(item,k-item, index, mydict[k-item], k)) return print("No, I couldn't find a pair for the given target.") # Fully correct fast version that returns pairs rather than printing. # # does not allow same item to be used twice (but does allow same # *number* to be used twice if appears more than once in the list) # # Returns False if no pair found # def findKPairFastCorrectReturn(inputList, k): global numberIndicesDict numberIndicesDict = {} # first, fill numberIndicesDict dictionary with items # number: [list of indices where that number occurs in inputList] # E.g. if dict contains 23: [6, 99], 23 appears at indices 6 and 99 in input for index in range(len(inputList)): number = inputList[index] if number in numberIndicesDict: numberIndicesDict[number].append(index) else: numberIndicesDict[number] = [index] # second, go through each number in input list, checking if suitable # second value, k-number, exists, repesenting pair (number, k-number) for index in range(len(inputList)): number = inputList[index] if (k-number) in numberIndicesDict: if k != 2*number: # two different numbers return (index, numberIndicesDict[k-number][0]) elif len(numberIndicesDict[number]) > 1: # can use same number twice *if* it appears in input list more than once return (numberIndicesDict[number][0], numberIndicesDict[number][1]) return False # even better and simpler def findKPairFastCorrectReturnV2(inputList, k): numberIndexDict = {} for index in range(len(inputList)): number = inputList[index] if (k-number) in numberIndexDict: return (index, numberIndexDict[k-number]) else: numberIndexDict[number] = index return False # If dictionaries not allowed/available BUT we know that numbers in # inputList are in a limited range, we can still solve the problem very quickly. # Basically, use a big list instead as a pseudo-dictionary. # # Note: this part version assumes numbers are >= 0 and <= 9999 # but that could easily be changed (just change the 10000 on the first line) # def findKPairFastLimited(inputList, k): maxNum = 10000 bigList = [False] * maxNum for index in range(len(inputList)): item = inputList[index] if (0 <= (k-item) < maxNum) and (bigList[k-item] != False): print("Yay! Found a pair ({:d}, {:d}) at ({}, {}) that sum to {:d}".format(item,k-item, index, bigList[k-item], k)) return bigList[item] = index print("No, I couldn't find a pair for the given target.") import random def mixup(L): newL = L[:] length = len(L) for i in range(length): newIndex = random.randint(i,length-1) newL[newIndex], newL[i] = newL[i], newL[newIndex] return(newL) def randomIntList(n, minInt, maxInt): result = [] for i in range(n): newInt = random.randint(minInt, maxInt) result.append(newInt) return(result) # create a list of n integers, each randomly selected from range -maxMag to maxMag # Then for each number 0...testRange test if the list contains a pair that sums to that number. # def testFindKPair(n, maxMag, testRange = 999): global l global results results = [] l = randomIntList(n, -maxMag, maxMag) numYes = 0 numNo = 0 for i in range(testRange): result = findKPairFastCorrectReturnV2(l, i) if not result: numNo = numNo + 1 else: results.append([i, l[result[0]], l[result[1]]]) numYes = numYes + 1 print("num yes: {}, num no: {}".format(numYes, numNo)) return results # First test on small lists both when pairs exist and don't # # Then test on # l = list(range(10000)) # findKPairSlow(l, 9999) # findKPairSlow(l,-1) # Then try similar queries on these: # l = list(range(20000)) # l = list(range(40000)) # Then try findKPairFast. E.g. # l = list(range(1000000)) # findKPairFast(l,-1) # You should see that the fast version is very fast for large lists both when # pair exists and doesn't exist. # For insight on Question 6 from lecture slides, try these: # l = randomIntList(10000, -1000000, 1000000) # findKPairFastCorrectReturnV2(l, 14) # findKPairFastCorrectReturnV2(l, 15) # res = testFindKPair(10, 1000000) # res = testFindKPair(100, 1000000) # res = testFindKPair(1000, 1000000) # res = testFindKPair(5000, 1000000) # res = testFindKPair(10000, 1000000) # res = testFindKPair(10000, 1000000, 10000) # res = testFindKPair(10000, 1000000, 100000) import time def timeKPair(theList, key, whichFunction = findKPairSlow1): tstart = time.time() whichFunction(theList, key) tend = time.time() print("Execution time:", tend-tstart)