4
\$\begingroup\$

I created the code for the problem description below. It works for\$N\le10^6\$ but after that it gives a time out error. What I don't understand is how to optimize the code using dynamic programming. I have used a dictionary to create decibinary numbers with the formula mentioned inthis OEIS post. for more description link:Hackerrank decibinary problem forum

Problem description:

Let's combine decimal and binary numbers in a new system we calldecibinary! In this number system, each digit ranges from 0 to 9 (likethe decimal number system), but the place value of each digitcorresponds to the one in the binary number system. For example, thedecibinary number 2016 represents the decimal number 24 because:

\$ (2016)_{decibinary}= 2\cdot2^3+0\cdot2^2+1\cdot2^1+6\cdot2^0 = (24)_{10} \$

Pretty cool system, right? Unfortunately, there's a problem: twodifferent decibinary numbers can evaluate to the same decimal value!For example, the decibinary number 2008 also evaluates to the decimalvalue 24:

\$ (2008)_{decibinary}= 2\cdot2^3+0\cdot2^2+0\cdot2^1+8\cdot2^0 = (24)_{10}\$

This is a major problem because our new number system has no realapplications beyond this challenge!

Consider an infinite list of non-negative decibinary numbers that issorted according to the following rules:

  • The decibinary numbers are sorted in increasing order of the decimal value that they evaluate to.
  • Any two decibinary numbers that evaluate to the same decimal value are ordered by increasing decimal value, meaning the equivalentdecibinary values are strictly interpreted and compared as decimalvalues and the smaller decimal value is ordered first.

You will be given q queries in the form of an integer, q. For each x,find and print the\$x^{\textrm{th}}\$ decibinary number in the list on a newline.

enter image description here

Function Description

Complete the decibinaryNumbers function in the editor below. For eachquery, it should return the decibinary number at that one-based index.

decibinaryNumbers has the following parameter(s):

x: the index of the decibinary number to return

Input Format

The first line contains an integer,\$q\$, the number of queries. Each ofthe next\$q\$ lines contains an integer,\$x\$, describing a query.

Constraints:

\$ 1 \le q \le 10^5 \$

\$ 1 \le x \le 10^{16} \$

Output Format

For each query, print a single integer denoting the the xth decibinarynumber in the list. Note that this must be the actual decibinarynumber and not its decimal value. Use 1-based indexing.

Sample Input 0

5123410

Sample Output 0

01210100

Additional samples omitted…

#!/bin/python3import mathimport osimport randomimport reimport sysfrom math import floorfrom collections import defaultdicta={0:0,1:1,2:2,3:3}for i in range(4,10**6):    a[i]=2*a[floor(i/10)]+i%10d=defaultdict(lambda:[])i=1x_to_decibin={}for x,y in a.items():    d[y].append(x)i=1l=list(d.values())l=sum(l,[])ind=[i for i in range(1,len(l)+1)]x_to_decibin=dict(zip(ind,l))print(x_to_decibin)    def decibinaryNumbers(x):    return x_to_decibin[x]if __name__ == '__main__':    fptr = open(os.environ['OUTPUT_PATH'], 'w')    q = int(input().strip())    for q_itr in range(q):        x = int(input().strip())        result = decibinaryNumbers(x)        fptr.write(str(result) + '\n')    fptr.close()
200_success's user avatar
200_success
146k22 gold badges191 silver badges481 bronze badges
askedAug 9, 2022 at 21:14
vijayalakshmi_bhagat's user avatar
\$\endgroup\$

1 Answer1

4
\$\begingroup\$

First there's some script cleanups I want to mention.

import mathimport osimport randomimport reimport sysfrom math import floorfrom collections import defaultdict

I'll assume that the imports from the default input may be needed, but I think it's pretty unnecessary to import floor in addition to math. Usuallymath.floor is short enough, but it's also not needed in this case.

a={0:0,1:1,2:2,3:3}for i in range(4,10**6):    a[i]=2*a[floor(i/10)]+i%10

Instead of using floor here, you can use a double division operator which will also do floored division. You also don't need adict for this purpose, and could instead just initialize a list to the size you need.

N = 10**6+1 # Max size to compute (plus one for offset)a = [0]*Nfor i in range(1,N):    a[i]=2*a[i//10]+i%10

d=defaultdict(lambda:[])i=1x_to_decibin={}for x,y in a.items():    d[y].append(x)i=1l=list(d.values())l=sum(l,[])

Thei=1 statements don't do anything here sincei is overwritten when next used.x_to_decibin is also set later. The intermediate list is also unneeded sincedict_values is iterable.

d=defaultdict(lambda:[])# Iterate through decibin values (index) and add them to a list of their decimal value (element)for index, element in enumerate(a):    d[element].append(index)l=sum(d.values(),[])

ind=[i for i in range(1,len(l)+1)]x_to_decibin=dict(zip(ind,l))print(x_to_decibin)def decibinaryNumbers(x):    return x_to_decibin[x]

I'm not sure if you included the print in a submission, but printing a fairly large structure won't be great.x_to_decibin also doesn't need to be created at all since you can just usel directly with a simple 1 offset.

def decibinaryNumbers(x):    return l[x - 1] # Get value from precomputed list (with 1 offset)

With all of these changes, the running time for the setup withN=10**6+1 on my machine went from 5.7 to 2.4 seconds. From here, profiling shows that by far the slowest part of the script is callingsum on the large iterable. Replacing it with aitertools.chain.from_iterable call sped it up to 0.15 seconds.

However, I am only able to increase N to about10**8 before memory starts becoming an issue. These changes allow for doing subtask 3 of10**7 pretty quickly, but precomputing every value is just not going to work for the full size. If you want to do that, you'll instead need something that generates a value at runtime. You'll have to change your method to do this though. Try following some of what people have put in the discussions.

Final code:

from itertools import chainfrom collections import defaultdictN = 10**6+1a = [0]*Nfor i in range(1,N):    a[i]=2*a[i//10]+i%10d=defaultdict(lambda:[])for index, element in enumerate(a):    d[element].append(index)l=tuple(chain.from_iterable(d.values()))def decibinaryNumbers(x):    return l[x - 1]
answeredAug 14, 2022 at 1:19
duckboycool's user avatar
\$\endgroup\$
2
  • \$\begingroup\$I didn't understand what they did in the discussion section. I only want to optimize this code. Anything above 10^6 gives me memory error.\$\endgroup\$CommentedAug 16, 2022 at 19:12
  • \$\begingroup\$Running it for 10^7 only used about 830MB on my machine, so I'd be a bit surprised to see aMemoryError there. You could try usingdel statements to deletea andd once done with them, and also usingarray.array('L') from thearray module instead of the current itereables fora,l, and and thedefaultdict lambda.\$\endgroup\$CommentedAug 16, 2022 at 19:56

You mustlog in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.