0

Here is my solution to the Lead Game problem on Codechef. It runs fine, but took 2.63 sec and 3.8M memory, while I saw many C programs that had completed in 0.08 seconds and 1.6M memory. How can I make it faster?

import sys
cnt = int(sys.stdin.readline())
match = [[int(x) for x in sys.stdin.readline().split()] for i in range(cnt)]
diff=[]
for i in range(cnt):
      if i!=0:
             match[i]=[sum(vals) for vals in zip(match[i-1],match[i])]
      diff.append([1 if max(match[i])==match[i][0] else 2,abs(match[i][0]-match[i][1])])
maxval = max(diff,key=lambda x:x[1])
sys.stdout.write(str(maxval[0]) + ' ' + str(maxval[1]))  
Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
mankand007
  • 952
  • 2
  • 10
  • 22
  • have you tried to run your code with profiler? What part is the most time-consuming? – dbf Jun 04 '12 at 14:15
  • @dbf: I didn't. How do I do that. It'd be great if you could point me somewhere. Thanks! – mankand007 Jun 04 '12 at 14:16
  • Are you opposed to using 3rd party packages (e.g. `numpy`)? I'd imaging you could get some speed performance there. – mgilson Jun 04 '12 at 14:18
  • 2.63sec for the few lines? Are you measuring the time to type the input too? The memory consumption is possible, since Python has more overhead than C, but the time doesn't seem realistic. – eumiro Jun 04 '12 at 14:18
  • @mankand007 check these links: http://docs.python.org/library/profile.html, http://stackoverflow.com/questions/582336/how-can-you-profile-a-python-script – dbf Jun 04 '12 at 14:19
  • Also, you could get rid of the test `if i!=0` by having the loop go: `for i in range(1,cnt)`. – mgilson Jun 04 '12 at 14:19
  • @mgilson: I'm just learning python and trying to optimize my coding style here. So, I would prefer an answer in the line of 'Dos and Don'ts', 'Best Practices', and sorts... And I can't skip the 0th element as I need to process that too. Only, I don't want to try and refer to its previous element. – mankand007 Jun 04 '12 at 14:19
  • you should add the input data for your code so we can test it – Facundo Casco Jun 04 '12 at 14:21
  • Too much array manipulation, I feel. You could do this without `zip` and `max` - it's just two numbers; two plusses and a comparison should suffice. – Amadan Jun 04 '12 at 14:22
  • But skipping the 0th element still allows you to access it with `i-1` as you already have. And it'll let you eliminate the `if` statement that will be executed every time through the loop but that is almost always `True`. – kindall Jun 04 '12 at 14:23
  • @eumiro: The input was given by the test system via sysin, and yes the code took that long from start to end. – mankand007 Jun 04 '12 at 14:23
  • @F.C: The input data is present in the link provided. I didn't give it here because it would seem irrelevent without the problem statement. Here it is `Input: ` `5` `140 82` `89 134` `90 110` `112 106` `88 90` `Output:` `1 58` – mankand007 Jun 04 '12 at 14:26
  • @mgilson: I'm not appending [1]. I'm appending a list of two elements: [largest among the two elements(1 or 2 being its position), difference between them] And the code runs fine and was accepted by the system for correct results – mankand007 Jun 04 '12 at 14:30
  • @mankand007 -- You're right. (my brain inserted an extra parnethesis on the rhs of the ternary operator) – mgilson Jun 04 '12 at 14:32
  • Also, your big problem: comparing C with Python :P It's rather obvious C will be faster and run in less memory. I will also bet that C will do it one row at a time, without loading the whole set into memory, without comprehensions, without `max`, only with comparisons, additions and one single "while not end of file" loop. While you could use some of the same techniques, you probably don't want to write Python in quite the same way as C. :P – Amadan Jun 04 '12 at 14:38
  • @Amadan: Well, Python might be slower than C, but there is also a huge factor called 'Programmer Efficiency'. I just want to improve that so that I can be worthier of my weapons in the battles.. :P – mankand007 Jun 05 '12 at 17:19
  • @mankand007: I agree with that wholeheartedly - I don't even remember when I last did something serious in a compiled language. However, you didn't compare a day on C code versus an hour on Python, you compared Python running in 2.63 sec while C ran in 0.08. All I'm saying is that that particular comparison is rather silly. Compare your Python with other people's Python; if you have to, compare to Ruby, Perl or Lua; but against C or compiled OCaml or C++, Python can't help but lose in execution time. – Amadan Jun 05 '12 at 18:41

2 Answers2

4

I wouldn't worry about the memory footprint (Python data structures take a little more space, and it's normal) and also it's hard to expect a Python script to beat a C program in terms of speed.

Edit: no need to keep leads history

My O(n) algorithm ran in 1.18 seconds:

import sys

rounds = int(sys.stdin.readline())

score = [0,0]
leads = [0,0]
while rounds > 0:
    results = map(int, sys.stdin.readline().split())
    score[0] += results[0]
    score[1] += results[1]
    lead = score[0] - score[1]
    if (lead < 0 and leads[1] < -lead): leads[1] = -lead
    if (lead > 0 and leads[0] < lead): leads[0] = lead
    rounds -= 1

if (leads[0] > leads[1]): print 1, leads[0]
else: print 2, leads[1]

Edit

To see where your algorithm spends most time you can use:

cat inputfile | python -m cProfile yourScript.py
wroniasty
  • 7,884
  • 2
  • 32
  • 24
1

Quick inspiration looks that you have O(n^2) algorithm, where you could use O(n) algorithm.

Instead of

 for:
    for: #this for  comes from line whit list comprehension

Just assemble one or multiple for loops (but not nested for loops).

It is not problem, that python si too slow, just your algorithm is not efficient enough

EDIT

I was wrong, maybe append is just too slow. Try using comprehension

so diff is just (out of for loop)

diff = [[1 if max(m)==m[0] else 2,abs(m[0]-m[1])] for m in match]

and use try to use tuples:

code is then.

import sys
cnt = int(sys.stdin.readline())
match = [tuple(int(x) for x in sys.stdin.readline().split()) for i in range(cnt)]
diff=[]
for i in range(cnt):
   if i!=0:
         match[i]=tuple(sum(vals) for vals in zip(match[i-1],match[i]))
diff = [tuple((1 if max(m)==m[0] else 2,abs(m[0]-m[1]))) for m in match]
maxval = max(diff,key=lambda x:x[1])
sys.stdout.write(str(maxval[0]) + ' ' + str(maxval[1])) 
Luka Rahne
  • 10,336
  • 3
  • 34
  • 56
  • 1
    If I'm reading the code right, the comprehension should always be over a 2 element array, so it's actually `O(2n)` (which is equivalent to `O(n)`). You can't measure complexity just by counting `for` statements, without seeing what they are doing. – Amadan Jun 04 '12 at 14:26
  • At lest line: match[i]=[sum(vals) for vals in zip(match[i-1],match[i])] run time is depended on N. And it runs N times. So in total O(N*N) time. – Luka Rahne Jun 04 '12 at 14:31
  • Yes, that's the line I'm talking about. I don't see how it depends on N. `match[i-1]` has 2 elements (`[playerAScore, playerBScore]`); `match[i]` has 2 elements. `zip` is 2 elements long, so it is `O(2)` for each row, not `O(n)`. I think `zip` then `max` is overkill, but it doesn't make it `O(n^2)`. – Amadan Jun 04 '12 at 14:33
  • The above code ran for 4.08 secs with 3.9M memory. Anyways, thanks for the suggestion, and +1 for advising to first determine the type of algorithm needed. – mankand007 Jun 05 '12 at 17:15