1

Please no built-ins besides len() or range(). I'm studying for a final exam.

Here's an example of what I mean.

def find_numbers(x, lst):


lst = [3, 8, 1, 2, 0, 4, 8, 5]

find_numbers(3, lst) # this should return -> (1, 6, 7)

I tried this not fully....couldn't figure out the best way of going about it:

def find_K_highest(lst, k):
 newlst = [0] * k
 maxvalue = lst[0]


 for i in range(len(lst)):
    if lst[i] > maxvalue:
        maxvalue = lst[i]
        newlst[0] = i
Jens Björnhager
  • 5,632
  • 3
  • 27
  • 47
VPNTIME
  • 721
  • 2
  • 8
  • 13
  • not much of an attempt when I was trying...couldnt really figure it out – VPNTIME May 12 '12 at 02:43
  • You need answer with only `len` or `range`? – akaRem May 12 '12 at 02:44
  • @akaRem those are the only built-ins allowed for use on the exam. – VPNTIME May 12 '12 at 02:45
  • 3
    @Omerta: It shouldn't be that difficult if you break it down into steps, and do it the algorithm in pseudocode. Think about how YOU (manually) would find the 3 highest items in a list. – Joel Cornett May 12 '12 at 02:46
  • 2
    Ugh, I hate it when teachers use those kind of restrictions. I get reimplementing features from the standard library makes thinking up simple tasks easier, but this kind of thing is just lazy and teaches bad practice. – Gareth Latty May 12 '12 at 03:02

4 Answers4

5

Take the first 3 (x) numbers from the list. The minimum value for the maximum are these. In your case: 3, 8, 1. Their index is (0, 1, 2). Build pairs of them ((3,0), (8,1), (1,2)).

Now sort them by size of the maximum value: ((8,1), (3,0), (1,2)).

With this initial List, you can traverse the rest of the list recursively. Compare the smallest value (1, _) with the next element in the list (2, 3). If that is larger (it is), sort it into the list ((8,1), (3,0), (2,3)) and throw away the smallest.

In the beginning you have many changes in the top 3, but later on, they get rare. Of course you have to keep book about the last position (3, 4, 5, ...) too, when traversing.

An insertion sort for the top N elements should be pretty performant.

Here is a similar problem in Scala but without the need to report the indexes.

Community
  • 1
  • 1
user unknown
  • 35,537
  • 11
  • 75
  • 121
1

I dont know is it good to post a solution, but this seems to work:

def find_K_highest(lst, k):
    # escape index error
    if k>len(lst):
        k=len(lst)
    # the output array
    idxs = [None]*k
    to_watch = range(len(lst))
    # do it k times
    for i in range(k):
        # guess that max value is at least at idx '0' of to_watch
        to_del=0
        idx = to_watch[to_del]
        max_val = lst[idx]
        # search through the list for bigger value and its index
        for jj in range(len(to_watch)):
            j=to_watch[jj]
            val = lst[j]
            # check that its bigger that previously finded max
            if val > max_val:
                idx = j
                max_val = val
                to_del=jj
            # append it
        idxs[i] = idx
        del to_watch[to_del]
        # return answer
    return idxs

PS I tried to explain every line of code.

akaRem
  • 7,326
  • 4
  • 29
  • 43
  • I think it works so as long every number in the list is positive and greater than 0. A list of negative numbers wouldn't work with that. Probably best to initialize max_val to lst[0]. – VPNTIME May 12 '12 at 03:04
  • @Omerta You are right. I added corrections. Also I've optimized code a little with `to_watch` – akaRem May 12 '12 at 03:17
  • 1
    This uses a bunch of list methods as well...`pop`, `append`, `__contains__` to name a few (I'm assuming `__getitem__` is ok -- you really can't do anything without that one). – mgilson May 12 '12 at 03:22
  • @mgilson I've replaced it with allowed built-ins – akaRem May 12 '12 at 03:27
  • There was problem with idx=0, i've fixed it – akaRem May 12 '12 at 03:32
  • @akaRem your code with the built-ins work. Your new code produces an out of index range. – VPNTIME May 12 '12 at 03:35
  • 1
    I think there's still an index issue here. The problem is that j is an item in `to_watch`, whose biggest item is `len(lst)`. So, `j` can be as big as `len(lst)`. `idx` gets assigned the value of `j`, then you remove that element from `to_watch`. if `j` (and therefore `idx` equals `len(lst)` and you've already deleted something, you'll have an index out of bounds. Also, I'm not clear if you're actually deleting the index you want to delete in the first place... – mgilson May 12 '12 at 03:36
  • @mgilson No, of course I delete item after iteration block. Plus, the biggest item of `to_watch` is `len(lst)-1`. – akaRem May 12 '12 at 03:40
  • 1
    @akaRem -- Yes, I mistyped. The largest item is `len(lst)-1`. But, the maximum index that you could ever possibly use in your `del` statement is `len(lst)-1` so my point is still the same. Just try your function on the input provided by the OP and you'll see (I get an index out of bounds error). – mgilson May 12 '12 at 03:44
  • For the example `lst = [3, 8, 1, 2, 0, 4, 8, 5]`, in first cycle `to_watch = [0,1,2,3,4,5,6,7]`, in the second: `to_watch = [0,2,3,4,5,6,7]` because `1` we alreade picked to result, so no need to iterate over it, on third cycle `to_watch = [0,2,3,4,5,7]` - we picked the second `8`, and after third cycle `to_watch = [0,2,3,4,5]` – akaRem May 12 '12 at 03:44
  • @akaRem The algorithm was mostly good. I edited to make it work. I hope you don't mind. – mgilson May 12 '12 at 03:47
  • 1
    @mgilson The algoritm won't work with `[9,1,2,3,4,5,6]`. The answer would be `[0, 0, 0]` - and it is not right. Guess why! – akaRem May 12 '12 at 03:50
  • 1
    I've corrected it. Now works perfectly! And even improved function. – akaRem May 12 '12 at 03:58
  • 1
    @akaRem -- You did it. Nice work. My only further suggestion is to create the list `idxs = [-1] * k`, and then instead of `idxs += [idx]`, `idx[k] = idx` -- This takes all the magic out of dynamically extending your list from an algorithm standpoint. Nice work. +1. – mgilson May 12 '12 at 04:07
  • @mgilson Thanks! But your comments were very constructive.. So WE did it. I apply your suggestion (but you mistyped a little: `idxs[i] = idx`) and `idxs = [-1]*k` are valuable, so i typed as `[None]*k` - just to reserve place. – akaRem May 12 '12 at 05:02
1

Can you use list methods? (e.g. append, sort, index?). If so, this should work (I think...)

def find_numbers(n,lst):
    ll=lst[:]
    ll.sort()
    biggest=ll[-n:]
    idx=[lst.index(i) for i in biggest] #This has the indices already, but we could have trouble if one of the numbers appeared twice
    idx.sort()
    #check for duplicates.  Duplicates will always be next to each other since we sorted.
    for i in range(1,len(idx)):
       if(idx[i-1]==idx[i]):
         idx[i]=idx[i]+lst[idx[i]+1:].index(lst[idx[i]]) #found a duplicate, chop up the input list and find the new index of that number
         idx.sort()
    return idx

lst = [3, 8, 1, 2, 0, 4, 8, 5]

print find_numbers(3, lst)
mgilson
  • 300,191
  • 65
  • 633
  • 696
  • list methods aren't allowed and neither are slices, even on exams. Teacher is insane about this stuff. – VPNTIME May 12 '12 at 03:14
  • @jamylak...it's a first semester computer science class...and all the prof cares about is learning algorithms. Prof doesnt care about syntax or built-ins. If it was up to the prof, the class would be taught in C. – VPNTIME May 12 '12 at 03:25
1

Dude. You have two ways you can go with this.

First way is to be clever. Phyc your teacher out. What she is looking for is recursion. You can write this with NO recursion and NO built in functions or methods:

#!/usr/bin/python

lst = [3, 8, 1, 2, 0, 4, 8, 5]

minval=-2**64

largest=[]

def enum(lst): 
    for i in range(len(lst)): 
        yield i,lst[i]

for x in range(3):
    m=minval
    m_index=None
    for i,j in enum(lst):
        if j>m: 
            m=j
            m_index=i

    if m_index:        
        largest=largest+[m_index]
        lst[m_index]=minval       

print largest  

This works. It is clever. Take that teacher!!! BUT, you will get a C or lower...

OR -- you can be the teacher's pet. Write it the way she wants. You will need a recursive max of a list. The rest is easy!

def max_of_l(l):
    if len(l) <= 1:
        if not l:
            raise ValueError("Max() arg is an empty sequence")
        else:
            return l[0]
    else:
        m = max_of_l(l[1:])
        return m if m > l[0] else l[0]

print max_of_l([3, 8, 1, 2, 0, 4, 8, 5])        
the wolf
  • 34,510
  • 13
  • 53
  • 71
  • Why so complicated non-pythonic enum? `def enum: for i in range(len(lst)): yield i,lst[i]` or even inline `enum = (i,lst[i] for i in range(len(lst)))`.But anyway enum is absolute equivalent for forbidden `enumerate`.. – akaRem May 12 '12 at 05:35
  • @akaRem: Could do that I suppose. I was not trying to be terse however. I like your mods... – the wolf May 12 '12 at 06:03