Project Euler Problem #103

Problem #103 concerns sets of integers with ordered sums of all possible subsets. The question reads:

Project Euler Problem 103: Special subset sums: optimum
Let S(A) represent the sum of elements in set A of size n. We shall call it a special sum set if for any two non-empty disjoint subsets, B and C, the following properties are true:
S(B) ≠ S(C); that is, sums of subsets cannot be equal.
If B contains more elements than C then S(B) > S(C).
If S(A) is minimised for a given n, we shall call it an optimum special sum set. The first five optimum special sum sets are given below.
n = 1: {1}
n = 2: {1, 2}
n = 3: {2, 3, 4}
n = 4: {3, 5, 6, 7}
n = 5: {6, 9, 11, 12, 13}
It seems that for a given optimum set, A = {a1a2, ... , an}, the next optimum set is of the form B = {ba1+ba2+b, ... ,an+b}, where b is the "middle" element on the previous row.
By applying this "rule" we would expect the optimum set for n = 6 to be A = {11, 17, 20, 22, 23, 24}, with S(A) = 117. However, this is not the optimum set, as we have merely applied an algorithm to provide a near optimum set. The optimum set for n = 6 is A = {11, 18, 19, 20, 22, 25}, with S(A) = 115 and corresponding set string: 111819202225.
Given that A is an optimum special sum set for n = 7, find its set string.
NOTE: This problem is related to Problem 105 and Problem 106.

This is possibly the most troll question in all of Project Euler. My solution technically isn’t a complete solution, but luckily it doesn’t need to be. Here’s my solution:

Solution #1: Engineer’s Induction Approach

Contrary to the problem’s advice, we can apply the “rule” another time on the optimal set for n = 6 to get a potential set for n = 7 of {20,31,38,39,40,42,45}. From here, I checked all 7-tuples with each element within 2 of the element for this potential set to find other sets with lower sums. Because no new sums were found, I concluded that the algorithmic set was the correct optimal set. Here’s an implementation of this approach in Python 2.7:

 '''
 Author: Walker Kroubalkian
 Brute Force Approach to Project Euler Problem #103
 '''
 
 import time
 
 def testOrder(a):
     a = sorted(a)
     first = []
     total = 0
     for x in a:
         first.append(total)
         total+=x
     first.append(total)
     back = []
     total = 0
     for x in a[::-1]:
         back.append(total)
         total+=x
     back.append(total)
     l = len(a)
     for x in range(1,l):
         if first[x+1]<back[x]:
             return False
     return True
 
 def subset(myList,n):
     if n==0:
         return [[]]
     l = len(myList)
     if l<n:
         return []
     a = myList[0]
     b = subset(myList[1:],n)
     c = subset(myList[1:], n-1)
     for x in c:
         x.append(a)
         b.append(sorted(x))
     return b
 
 def testNoneEqual(a):
     l = len(a)
     allSums = []
     for x in range(1,l+1):
         v = subset(a,x)
         for y in v:
             value = sum(y)
             if value in allSums:
                 return False
             allSums.append(value)
     return True
 
 def isSpecialSet(myList):
     if testOrder(myList):
         if testNoneEqual(myList):
             return True
     return False
 
 def allPossible(myList,length):
     if length==0:
         return [[]]
     v = allPossible(myList, length-1)
     total = []
     for a in myList:
         for x in v:
             t = x[:]
             t.append(a)
             total.append(t)
     return total
 
 def projectEulerProblemOneHundredThree():
     bestKnown = [20,31,38,39,40,42,45]
     l = len(bestKnown)
     potential = True
     possible = [-2,-1,0,1,2]
     addition = allPossible(possible, l)
     final = []
     for a in addition:
         if sum(a)<0:
             final.append(a)
     while(potential):
         potential = False
         for a in final:
             t = bestKnown[:]
             for x in range(l):
                 t[x]+=a[x]
             if isSpecialSet(t):
                 potential = True
                 bestKnown = t
                 break
     s = ""
     for x in bestKnown:
         s+=str(x)
     return s
 
 start = time.time()
 print projectEulerProblemOneHundredThree()
 print ("--- %s seconds ---" % (time.time()-start))
 
 '''
 Prints
 
 20313839404245
 --- 0.617619991302 seconds ---
 
 for input of given problem.
 ''' 

And with that, we’re done. I found it pretty funny that they explained the rule and gave one counterexample when the solution involved simply applying the rule once to the counterexample. This problem would probably be considerably more difficult if they hadn’t explained the rule in the problem statement.

Thanks for reading! See you tomorrow.

Project Euler Problem #102

Problem #102 concerns determining whether a point is within the triangle formed by a triple of coordinates. The question reads:

Project Euler Problem 102: Triangle containment
Three distinct points are plotted at random on a Cartesian plane, for which -1000 ≤ xy ≤ 1000, such that a triangle is formed.
Consider the following two triangles:
A(-340,495), B(-153,-910), C(835,-947)
X(-175,41), Y(-421,-714), Z(574,-645)
It can be verified that triangle ABC contains the origin, whereas triangle XYZ does not.
Using triangles.txt (right click and 'Save Link/Target As...'), a 27K text file containing the co-ordinates of one thousand "random" triangles, find the number of triangles for which the interior contains the origin.
NOTE: The first two examples in the file represent the triangles in the example given above.

I must confess, I am unsure whether my solution will get the correct answer for all possible inputs. Regardless, here is my solution:

Solution #1: Relative Position Approach

First I implemented two methods that determine whether an arbitrary line on the coordinate plane passes above or below and to the left or to the right of the origin. Using these methods, I could determine how the three lines formed by the sides of the triangle were positioned relative to the origin.

Next, I translated the triangle such that its new centroid was at the origin and performed the same operations to check how the translated triangle was positioned relative to the origin. Finally, I concluded that if the original relative position was equivalent to the translated relative position, the translation did not change whether the origin was within the triangle. Because in the second case, the origin is the centroid which is within the triangle, it can be concluded that the original triangle contained the origin. Here is an implementation of this approach in Python 2.7:

 '''
 Author: Walker Kroubalkian
 Analytic Geometry Approach to Project Euler Problem #102
 '''
 
 import time
 f = open("PE102Triangles.txt","r")
 
 if f.mode=="r":
     contents = f.readlines()
     realContents = []
     for x in contents:
         coords = map(int, x.split(","))
         realContents.append([[coords[0],coords[1]],[coords[2],coords[3]],[coords[4],coords[5]]])
 else:
     raise ValueError("Cannot read from file")
 
 def containsOrigin(A,B,C):
     d = aboveBelowOrigin(A, B)
     e = aboveBelowOrigin(B, C)
     f = aboveBelowOrigin(C, A)
     g = leftRightOrigin(A, B)
     h = leftRightOrigin(B, C)
     i = leftRightOrigin(C, A)
     return [[d,g],[e,h],[f,i]] == relCentroid(A, B, C)
 
 def relCentroid(A,B,C):
     centroidX = (A[0]+B[0]+C[0])/3.0
     centroidY = (A[1]+B[1]+C[1])/3.0
     newA = [A[0]-centroidX,A[1]-centroidY]
     newB = [B[0]-centroidX,B[1]-centroidY]
     newC = [C[0]-centroidX,C[1]-centroidY]
     d = aboveBelowOrigin(newA, newB)
     e = aboveBelowOrigin(newB, newC)
     f = aboveBelowOrigin(newC, newA)
     g = leftRightOrigin(newA, newB)
     h = leftRightOrigin(newB, newC)
     i = leftRightOrigin(newC, newA)
     return [[d,g],[e,h],[f,i]]
     
 def aboveBelowOrigin(A,B):
     # Left = False, Right = True
     c = A[0]
     d = A[1]
     e = B[0]
     f = B[1]
     if c==e and d==f:
         return "ONLINE"
     if(c==e):
         return c>0
     if c*f==d*e:
         return "ONLINE"
     if c>e:
         return c*f>d*e
     return c*f<d*e
     
 def leftRightOrigin(A,B):
     # Below = False, Above = True
     c = A[0]
     d = A[1]
     e = B[0]
     f = B[1]
     if c==e and d==f:
         return "ONLINE"
     if(d==f):
         return f>0
     if c*f==d*e:
         return "ONLINE"
     if(d>f):
         return e*d>c*f
     return c*f>d*e
 
 def projectEulerProblemOneHundredTwo(myList):
     total = 0
     for x in myList:
         A = x[0]
         B = x[1]
         C = x[2]
         if containsOrigin(A, B, C):
             total+=1
     return total
 
 start = time.time()
 print projectEulerProblemOneHundredTwo(realContents)
 print ("--- %s seconds ---" % (time.time()-start))
 
 '''
 Prints
 
 228
 --- 0.00690007209778 seconds ---
 
 for input of given list of triangles.
 ''' 

And with that, we’re done. This approach makes intuitive sense, but I am unsure how to prove that it will always get the correct solution. Regardless, it was more than efficient enough to check the 1000 triangles in this problem.

Thanks for reading! See you tomorrow.

Project Euler Problem #101

Problem #101 concerns polynomials that approximate higher degree polynomials based on consecutive outputs. The question reads:

Project Euler Problem 101: Optimum polynomial
If we are presented with the first k terms of a sequence it is impossible to say with certainty the value of the next term, as there are infinitely many polynomial functions that can model the sequence.
As an example, let us consider the sequence of cube numbers. This is defined by the generating function,
un = n3: 1, 8, 27, 64, 125, 216,...
Suppose we were only given the first two terms of this sequence. Working on the principle that "simple is best" we should assume a linear relationship and predict the next term to be 15 (common difference 7). Even if we were presented with the first three terms, by the same principle of simplicity, a quadratic relationship should be assumed.
We shall define OP(k, n) to be the nth term of the optimum polynomial generating function for the first k terms of a sequence. It should be clear that OP(k, n) will accurately generate the terms of the sequence for n ≤ k, and potentially the first incorrect term (FIT) will be OP(k, k+1); in which case we shall call it a bad OP (BOP).
As a basis, if we were only given the first term of sequence, it would be most sensible to assume constancy; that is, for n ≥ 2, OP(1, n) = u1.
Hence we obtain the following OPs for the cubic sequence:
OP(1, n) = 1   1, 1, 1, 1, ...
OP(2, n) = 7n−6   1, 8, 15, ...
OP(3, n) = 6n2−11n+6   1, 8, 27, 58, ...
OP(4, n) = n3   1, 8, 27, 64, 125, ...
Clearly no BOPs exist for k ≥ 4.
By considering the sum of FITs generated by the BOPs (indicated in red above), we obtain 1 + 15 + 58 = 74.
Consider the following tenth degree polynomial generating function:
un = 1 − n + n2 − n3 + n4 − n5 + n6 − n7 + n8 − n9 + n10
Find the sum of FITs for the BOPs.

Much like 2019 Putnam Problem B5, this question can be solved most easily with finite differences. Here’s my solution:

Solution #1: Finite Differences Approach

Effectively, the values of the BOPs can be determined with the Finite Difference Method. By evaluating these values until an FIT is encountered, it is possible to find the sum of the FITs of all of the BOPs. Here is an implementation of this approach in Python 2.7:

 '''
 Author: Walker Kroubalkian
 Brute Force Approach to Project Euler Problem #101
 '''
 
 import time
 
 def evaluatePolynomial(poly,n):
     power = 1
     total = 0
     for x in poly:
         total+=power*x
         power*=n
     return total
 
 def partialDifferenceNextTerm(myList):
     l = len(myList)
     pyramid = [myList]
     for x in range(1,l):
         row = []
         theRow = pyramid[x-1]
         for z in range(len(theRow)-1):
             row.append(theRow[z+1]-theRow[z])
         pyramid.append(row)
     total = 0
     for x in pyramid:
         total+=x[len(x)-1]
     return total

 def projectEulerProblemOneHundredOne(poly):
     degree = len(poly)-1
     values = []
     for x in range(1,degree+3):
         values.append(evaluatePolynomial(poly, x))
     total = 0
     for x in range(1,degree+1):
         total+=partialDifferenceNextTerm(values[0:x])
     return total
 
 start = time.time()
 print projectEulerProblemOneHundredOne([1,-1,1,-1,1,-1,1,-1,1,-1,1])
 print ("--- %s seconds ---" % (time.time()-start))
 
 '''
 Prints
 
 37076114526
 --- 9.67979431152e-05 seconds ---
 
 for input of [1,-1,1,-1,1,-1,1,-1,1,-1,1]
 ''' 

And with that, we’re done. This is a great example of how knowing math can make Project Euler problems much easier.

Thanks for reading! See you tomorrow.

2019 Putnam Problem B2

On Saturday, December 7, 2019, I took the 2019 Putnam competition. The Putnam competition is the premier undergraduate math competition in the United States. It has two sessions of six problems each with 3 hours allotted for each session. The problems are all proof-based and are graded on a 0-10 scale, with 10 points being given only for complete proofs. This exam is infamous for having a median score of 0-1 points out of a total of 120. This was my first time taking the Putnam, so my goal was simply to get a positive score.

According to AoPS, discussion is now allowed on the 2019 Putnam.

Putnam Problem B2 concerns evaluating a complex limit. The question reads:

2019 Putnam Problem B2:
For all n>=1, let a_n = sum_(k=1)^(n-1) (sin((2k-1)pi/(2n)))/(cos^2((k-1)pi/(2n))cos^2(kpi/(2n))).
Determine lim_(n->inf) a_n/n^3.

I apologize if it is difficult to read the problem above. The link I provided has LaTeX for this problem. This problem was particularly satisfying because it gave me a chance to put an obscure trig identity I learned in my Physics class to use. Here’s my solution:

Solution #1: Telescoping L’Hopital Approach

We can recall that cos(x)+cos(y) = 2*cos((x+y)/2)*cos((y-x)/2) and that cos(x)-cos(y) = 2*sin((x+y)/2)*sin((y-x)/2). It follows that cos^2(x) – cos^2(y) = 4*cos((x+y)/2)*sin((x+y)/2)*cos((y-x)/2)*sin((y-x)/2) = sin(x+y)*sin(y-x). It follows that 1/(cos^2((k-1)pi/(2n))) – 1/(cos^2(kpi/(2n))) = sin((2k-1)pi/(2n))*sin(pi/2n)/(cos^2((k-1)pi/(2n))*cos^2(kpi/(2n))). With this substitution, it is possible to telescope the series to get that the given series a_n is equivalent to (1 – 1/(cos^2((n-1)pi/(2n)))/(sin(pi/2n)). At this point, we can use L’Hopital’s Rule to quickly find the limit as n approaches infinity of a_n/(n^3). We get that the answer is 8/pi^3 as desired.

I was pretty satisfied when this worked. I have almost never used these trig identities before, so seeing them simplify such a complex series was pretty great.

Thanks for reading! See you tomorrow.

2019 Putnam Problem A2

On Saturday, December 7, 2019, I took the 2019 Putnam competition. The Putnam competition is the premier undergraduate math competition in the United States. It has two sessions of six problems each with 3 hours allotted for each session. The problems are all proof-based and are graded on a 0-10 scale, with 10 points being given only for complete proofs. This exam is infamous for having a median score of 0-1 points out of a total of 120. This was my first time taking the Putnam, so my goal was simply to get a positive score.

According to AoPS, discussion is now allowed on the 2019 Putnam.

Putnam Problem A2 concerns finding angles in a triangle so that the line between the incenter and the centroid is parallel to one of the sides. The question reads:

2019 Putnam Problem A2:
In the triangle ABC, let G be the centroid, and let I be the center of the inscribed circle. Let alpha and beta be the angles at the vertices A and B, respectively. Suppose that the segment IG is parallel to AB and that beta = 2*tan^-1(1/3). Find alpha.

My solution for this problem was by far the least elegant of all of the solutions I wrote on the day of the competition. Here’s my solution:

Solution #1: Coordinate Bash Approach

WLOG, let AB = 1, let B be at (0,0), and let A be at (1,0). By the tangent addition formula, tan(beta) = (1/3+1/3)/(1-(1/3)*(1/3)) = 3/4. It follows that C has coordinates (4c,3c) for some real number c, and therefore G has coordinates (1+4c/3,c). Similarly, I has coordinates (3i,i) for some real number i. It is also known that I lies on the angle bisector of angle BAC. Thus, 2*tan^-1(i/(1-3i)) = tan^-1(3c/(1-4c)). Using the tangent addition formula, 3c/(1-4c) = (2i/(1-3i))/(1-(i/(1-3i))^2) = (2i(1-3i))/(1-6i+8i^2). In order for IG to be parallel to AB, we must have i = c. Setting i = c in this equation and solving, we get c = 1/4. It follows that C has coordinates (1,3/4), and therefore alpha = BAC = 90 degrees.

This was the initial solution I wrote during the competition. Luckily, while checking I noticed that c = 1/4 makes many of the expressions in the equation undefined. As a result, I added the note that this solution only proves that all values not equal to c = 1/4 result in bad triangles, and I showed that when c = 1/4, the property is satisfied.

Overall, this problem was relatively straightforward for a 2nd Problem on the Putnam, and it was actually the first problem I solved on the day of the competition. I really hope my coordinate bash is accepted, I am a bit nervous that my argument about c = 1/4 being the only solution will not be accepted.

Thanks for reading! See you tomorrow.

2019 Putnam Problem B1

On Saturday, December 7, 2019, I took the 2019 Putnam competition. The Putnam competition is the premier undergraduate math competition in the United States. It has two sessions of six problems each with 3 hours allotted for each session. The problems are all proof-based and are graded on a 0-10 scale, with 10 points being given only for complete proofs. This exam is infamous for having a median score of 0-1 points out of a total of 120. This was my first time taking the Putnam, so my goal was simply to get a positive score.

According to AoPS, discussion is now allowed on the 2019 Putnam.

Putnam Problem B1 concerns finding the number of squares that can be formed by integer solutions to the equation x^2+y^2 = 2^k. The question reads:

2019 Putnam Problem B1:
Denote by Z^2 the set of all points (x,y) in the plane with integer coordinates. For each integer n>=0, let P_n be the subset of Z^2 consisting of the point (0,0) together with all points (x,y) such that x^2+y^2 = 2^k for some integer k<=n. Determine, as a function of n, the number of four-point subsets of P_n whose elements are the vertices of a square.

This question was a bit of a pain to write up. I’m still not sure if my solution is rigorous enough for the Putnam. Regardless, this was probably the easiest question on the test to solve. Here’s my solution:

Solution #1: Bijection and Induction Approach

I started by showing that there exists a bijection between integer solutions (x,y) to x^2+y^2=n and integer solutions (x’,y’) to x’^2+y’^2 = 2n. It is easy to see that (x’,y’) = (x+y,x-y) works in the forward direction as (x+y)^2+(x-y)^2 = 2x^2+2y^2 = 2n. It is also easy to see that (x,y) = ((x’+y’)/2,(x’-y’)/2) works in the backward direction as x’^2+y’^2 = 2n implies that x’ and y’ have the same parity. It follows that the bijection is complete.

With this bijection, it is easy to show by induction that for exponents k of the form k = 2y, the only solutions are (2^y,0), (0,2^y), (0,-2^y), and (-2^y,0) and for exponents k of the form k = 2y+1, the only solutions are (2^y,2^y), (2^y,-2^y), (-2^y,2^y), and (-2^y,-2^y).

Now, we claim that the answer is 1+5n for all nonnegative integers n. Clearly it is true for n = 0. It suffices to show that for all n, P_n+1 has 5 more squares than P_n. Because adding new points to P_n will not change the number of squares within P_n, it suffices to only count the squares that include the new points. We can notice that these new 4 solutions will always form 5 new squares: one boundary square which connects the 4 points and 4 inner squares which contain one of the new solutions and the origin. To prove that these are the only 5 new squares, it suffices to show that the 4 new solutions form a boundary square and that if a sixth square existed, its points would need to be on the boundary square to form a right angle with one of the boundary points. All squares of this form have already been counted, so indeed, the answer is 1+5n.

As I mentioned above, I am a bit worried about whether this will be rigorous enough for the Putnam graders. It was quite easy to get the right answer, but proving it took quite a while, even if the entire process was relatively trivial. As a side note, it appears that some people were trolled by this question and got the wrong answer 1+n by forgetting the squares with the origin. Regardless, this was still probably the most approachable question this year.

Thanks for reading! See you tomorrow.

2019 Putnam Problem A1

On Saturday, December 7, 2019, I took the 2019 Putnam competition. The Putnam competition is the premier undergraduate math competition in the United States. It has two sessions of six problems each with 3 hours allotted for each session. The problems are all proof-based and are graded on a 0-10 scale, with 10 points being given only for complete proofs. This exam is infamous for having a median score of 0-1 points out of a total of 120. This was my first time taking the Putnam, so my goal was simply to get a positive score.

According to AoPS, discussion is now allowed on the 2019 Putnam.

Putnam Problem A1 concerns finding the possible values of an integer expression for arbitrary choices of the integers in the expression. The question reads:

2019 Putnam Problem A1:
Determine all possible values of
A^3 + B^3 + C^3 - 3ABC
where A, B, and C are nonnegative integers.

This was easily my favorite problem out of the questions I solved during the test. It took me much longer than it probably should have in reflection. Here’s my solution:

Solution #1: Mod 3 Bash

First, we can observe by AM-GM that (A^3+B^3+C^3)/3 >= (A^3*B^3*C^3)^(1/3) –> A^3+B^3+C^3 – 3ABC >= 0. Thus, the expression must take on a nonnegative integer for nonnegative choices of A, B, and C. To be concise, let f(A,B,C) = A^3+B^3+C^3 – 3ABC. We can observe that for all integers n, f(n+1,n,n-1) = 9n, f(n+1,n,n) = 3n+1, and f(n,n,n-1) = 3n-1. Finally, f(0,0,0) = 0. It follows that all nonnegative integers which are 0,1,2,4,5,7,8 mod 9 can be obtained with one of these generators. We claim these are the only possible values of f(A,B,C).

At this point, it suffices to show that f(A,B,C) cannot be 3 or 6 mod 9. We can observe that 0^3 = 3^3 = 6^3 = 0 mod 9, 1^3 = 4^3 = 7^3 = 1 mod 9, and 2^3 = 5^3 = 8^3 = 8 mod 9. It follows that A^3 mod 9 is uniquely determined by A mod 3. Similarly, 3ABC mod 9 = 3*(ABC mod 3) mod 9. Thus, the value of f(A,B,C) mod 9 is uniquely determined by A mod 3, B mod 3, and C mod 3. Thus, it suffices to check all triples A,B,C mod 3 and ensure that none are 3 mod 9 or 6 mod 9. We can simplify this task by noting that f is symmetric in A,B,C, so it is unnecessary to check rearrangements of A,B,C. Checking the triples (0,0,0), (0,0,1), (0,0,2), (0,1,1), (0,1,2), (0,2,2), (1,1,1), (1,1,2), (1,2,2), and (2,2,2), we can observe that none are 3 or 6 mod 9. Thus, we have shown that the only possible values of f(A,B,C) are all nonnegative integers which are not 3 or 6 mod 9.

I had a feeling there was a way to factor f(A,B,C), and indeed there is: f(A,B,C) = (A+B+C)(A^2+B^2+C^2-AB-AC-BC). Many solutions on AoPS used this factorization to greatly simplify the casework of the mod 3 bash.

This was probably my favorite question that I solved during the exam, because it took me nearly an hour to find a method that seemed promising. I initially failed to treat this problem as a Functional Equation and searched endlessly for potential factorizations. When I started trying to search for generators, I was very satisfied with the results.

Thanks for reading! See you tomorrow.

ASU SoDA Coding Challenge VI Medium Question #5

On Sunday, November 24, 2019, I participated in the ASU SoDA Coding Challenge VI. The ASU Software Developer’s Association (SoDA) is a club that is dedicated to teaching aspiring programmers how to solve complex problems that will prepare them for industry. The club runs recruiting events, Hackathons, Interview Prep sessions, and other activities that I have personally found invaluable during my first semester at ASU. I would strongly recommend anyone with a remote interest in computer science to join the club.

The SoDA Coding Challenge had 15 questions which were divided into three categories of three questions based on their respective difficulty levels. The Easy Questions, the Medium Questions, and the Hard Questions were made available to the contestants through Google Forms, and participants would have proctors manually check their code to make sure their programs worked.

Medium Question #5 involves determining paths along a weighted tree with certain properties. The question reads:

ASU SoDA Coding Challenge VI Medium Question 5: k-tree
Quite recently a creative student Lesha had a lecture on trees. After the lecture Lesha was inspired and came up with the tree of his own which he called a k-tree.
A k-tree is an infinite rooted tree where:
each vertex has exactly k children;
each edge has some weight;
if we look at the edges that goes from some vertex to its children (exactly k edges), then their weights will equal 1, 2, 3, ..., k.
The picture below shows a part of a 3-tree.
undefined
As soon as Dima, a good friend of Lesha, found out about the tree, he immediately wondered: "How many paths of total weight n (the sum of all weights of the edges in the path) are there, starting from the root of a k-tree and also containing at least one edge of weight at least d?".
Help Dima find an answer to his question. As the number of ways can be rather large, print it modulo 1000000007 (109 + 7).
Input
Three integers: n, k and d (1 ≤ n, k ≤ 100; 1 ≤ d ≤ k).
Output
Print a single integer — the answer to the problem modulo 1000000007 (10^9 + 7).
Test Cases:
input
3 3 2
output
3
input
3 3 3
output
1
input
4 3 2
output
6
input
4 5 2
output
7

This was easily my favorite question at this event, and based on the discussion after the event, I think a lot of people wanted to know a good solution for this question. Here’s the solution I used during the event:

Solution #1: Horrible Recursive Approach

During the event, I couldn’t think of a nice way to do this problem dynamically, so I used recursion. The recursive step stores the minimum maximum element that needs to be included in each path as well as the remaining sum of the path, and a bunch of conditions are given as the base case where the recursion ends. I don’t really feel like explaining it, so here’s an implementation of this in Python 2.7 that I used on the day of the competition:

 '''
 Author: Walker Kroubalkian
 SoDA Coding Challenge VI Medium Problem #5
 https://docs.google.com/document/d/14sk0u9ZVM1rrIsSCSwIWesI1ks8BukFMaT-NlJCr6FQ/edit
 '''
 
 import time
 import sys
 
 i = 0
 for x in sys.stdin:
     if i==0:
         n = int(x)
     elif i==1:
         k = int(x)
     else:
         d = int(x)
     i+=1
 
 def nChooseR(n,r):
     num = 1
     for i in range(r):
         num*=(n-i)
         num/=(i+1)
     return num
 
 myMod = 10**9 + 7
 
 def doProblem(n,k,d):
     if(k<d):
         return 0
     if(n<d):
         return 0
     if(n==1 and d==1):
         return 1
     total = 0
     if(n<=k):
         total+=1
     for x in range(1,d):
         total+=doProblem(n-x, k, d)
         total%=myMod
     for x in range(d,k+1):
         total+=doProblem(n-x, k, 1)
         total%=myMod
     return total
 
 start = time.time()
 print doProblem(n, k, d)
 print ("--- %s seconds ---" % (time.time()-start))
 
 '''
 Prints
 
 7
 --- 2.00271606445e-05 seconds ---
 
 for input of n = 4, k = 5, d = 2.
 ''' 

This manages to solve the problem for all of the sample test cases, but for the maximum input size given in the problem, it would take until the end of the universe for the program to execute. Luckily, on the day of the competition, the test cases they used to check our code for this problem were relatively small so this program seemed efficient enough. I feel a bit bad for using such inefficient code in a competition, but luckily I found a much faster solution after the fact. Here’s a more efficient solution:

Solution #2: Dynamic O(n) Approach

We can observe that this is simply the number of ordered partitions of n where the summands are at most k minus the number of ordered partitions of n where the summands are at most m-1. A well known algorithm can be used to find each of these values recursively in O(n) time complexity. Here is an implementation of this approach in Python 2.7:

 '''
 Author: Walker Kroubalkian
 SoDA Coding Challenge VI Medium Problem #5
 https://docs.google.com/document/d/14sk0u9ZVM1rrIsSCSwIWesI1ks8BukFMaT-NlJCr6FQ/edit
 '''
 
 import time
 import sys
 
 i = 0
 for x in sys.stdin:
     if i==0:
         n = int(x)
     elif i==1:
         k = int(x)
     else:
         d = int(x)
     i+=1
 
 myMod = 10**9 + 7
 
 def nChooseR(n,r):
     num = 1
     for i in range(r):
         num*=(n-i)
         num/=(i+1)
     return num
 
 def countOrderedPartitions(n,m):
     if(m<=0):
         return 0
     if(m>n):
         m = n
     values = []
     for _ in range(m-1):
         values.append(0)
     values.append(1)
     total = 1
     l = m
     for _ in range(n):
         values.append(total)
         total+=total
         total-=values[l-m]
         l+=1
     return values[l-1]
 
 def doProblem(n,k,d):
     if(n<d or k<d):
         return 0
     return (countOrderedPartitions(n, k) - countOrderedPartitions(n, d-1))%myMod
 
 start = time.time()
 print doProblem(n, k, d)
 print ("--- %s seconds ---" % (time.time()-start))
 
 '''
 Prints
 
 881051483
 --- 8.41617584229e-05 seconds ---
 
 for input of n = 100, k = 5, d = 2.
 ''' 

I’m really kicking myself for not finding this approach on the day of the competition as many of the Project Euler problems I have solved have used this exact approach. Regardless, this was by far the most interesting problem I solved on the day of the competition.

Thanks for reading! See you tomorrow.

ASU SoDA Coding Challenge VI Medium Question #4

On Sunday, November 24, 2019, I participated in the ASU SoDA Coding Challenge VI. The ASU Software Developer’s Association (SoDA) is a club that is dedicated to teaching aspiring programmers how to solve complex problems that will prepare them for industry. The club runs recruiting events, Hackathons, Interview Prep sessions, and other activities that I have personally found invaluable during my first semester at ASU. I would strongly recommend anyone with a remote interest in computer science to join the club.

The SoDA Coding Challenge had 15 questions which were divided into three categories of three questions based on their respective difficulty levels. The Easy Questions, the Medium Questions, and the Hard Questions were made available to the contestants through Google Forms, and participants would have proctors manually check their code to make sure their programs worked.

Medium Question #4 involves determining whether one string can be formed by rearranging some of the characters in another string. The question reads:

ASU SoDA Coding Challenge VI Medium Question 4: The Letter
Vasya decided to write an anonymous letter cutting the letters out of a newspaper heading. He knows heading s1 and text s2 that he wants to send. Vasya can use every single heading letter no more than once. Vasya doesn't have to cut the spaces out of the heading — he just leaves some blank space to mark them. Help him; find out if he will manage to compose the needed text.
Input
The first input string contains a newspaper heading s1. The second input string contains the letter text s2. s1 и s2 are non-empty lines consisting of spaces, uppercase and lowercase Latin letters, whose lengths do not exceed 200 symbols. The uppercase and lowercase letters should be differentiated. Vasya does not cut spaces out of the heading.
 
Output
If Vasya can write the given anonymous letter, print YES, otherwise print NO
 
Test Cases:
input
Instead of dogging Your footsteps it disappears but you dont notice anything
where is your dog
output
NO
input
Instead of dogging Your footsteps it disappears but you dont notice anything
Your dog is upstears
output
YES
input
Instead of dogging your footsteps it disappears but you dont notice anything
Your dog is upstears
output
NO
input
abcdefg hijk
k j i h g f e d c b a
output
YES

This question is pretty standard for those familiar with Python dictionaries. Here’s my solution:

Solution #1: Dictionary Approach

We form a dictionary of the letters in each string where the key is the letter and the value is the number of the time the letter appears. Then we check all characters in the letter and make sure that the frequency in the letter is not greater than the frequency in the newspaper. Here’s an implementation of this approach in Python 2.7:

 '''
 Author: Walker Kroubalkian
 SoDA Coding Challenge VI Medium Problem #4
 https://docs.google.com/document/d/14sk0u9ZVM1rrIsSCSwIWesI1ks8BukFMaT-NlJCr6FQ/edit
 '''
 
 import time
 import sys
 
 i = 0
 for x in sys:
     if(i==0):
         s1 = x
     else:
         s2 = x
     i+=1
 
 def dictString(s):
     myDict = {}
     for x in s:
         if x in myDict:
             myDict[x]+=1
         else:
             myDict.update({x : 1})
     return myDict
 
 def letterPossible(s1,s2):
     a1 = dictString(s1)
     a2 = dictString(s2)
     for x in a2:
         if(x!=" "):
             if x in a1:
                 if(a1[x]<a2[x]):
                     return "NO"
             else:
                 return "NO"
     return "YES"
 
 start = time.time()
 print letterPossible(s1, s2)
 print ("--- %s seconds ---" % (time.time()-start))
 
 '''
 Prints
 
 YES
 --- 3.50475311279e-05 seconds ---
 
 for input of s1 = "Instead of dogging Your footsteps it disappears but you dont notice anything", s2 = "Your dog is upstears"
 ''' 

And with that, we’re done. This was a relatively standard problem. Luckily, the next problem was far less generic.

Thanks for reading! See you tomorrow.

ASU SoDA Coding Challenge VI Medium Question #3

On Sunday, November 24, 2019, I participated in the ASU SoDA Coding Challenge VI. The ASU Software Developer’s Association (SoDA) is a club that is dedicated to teaching aspiring programmers how to solve complex problems that will prepare them for industry. The club runs recruiting events, Hackathons, Interview Prep sessions, and other activities that I have personally found invaluable during my first semester at ASU. I would strongly recommend anyone with a remote interest in computer science to join the club.

The SoDA Coding Challenge had 15 questions which were divided into three categories of three questions based on their respective difficulty levels. The Easy Questions, the Medium Questions, and the Hard Questions were made available to the contestants through Google Forms, and participants would have proctors manually check their code to make sure their programs worked.

Medium Question #3 involves determining all numbers in a given range with no repeating digits. The question reads:

ASU SoDA Coding Challenge VI Medium Question 3: L and R
You have two integers l and r. Find an integer x which satisfies the conditions below:
l≤x≤r
l≤x≤r.
All digits of x are different.
If there are multiple answers, print ALL of them.
Input:
Two parameters l and r s.t. l < x < r.  l > 0, r < 10^5
Output:
If an answer exists, print all of them. Otherwise, print −1.
Test Cases:
input
121 130
output
123 124 125 126 127 128 129
input
98766 100000
output
-1

During the competition, there was a bit of confusion about this question because it was unclear whether all numbers in the range should be printed or just one number. It was officially decided that all numbers needed to be printed, so there is not much optimization to be done in this solution. Here’s my solution:

Solution #1: Basic Iteration Approach

We simply iterate through all integers from L to R and print all numbers with the given property. Here is an implementation of this approach in Python 2.7:

 '''
 Author: Walker Kroubalkian
 SoDA Coding Challenge VI Medium Problem #3
 https://docs.google.com/document/d/14sk0u9ZVM1rrIsSCSwIWesI1ks8BukFMaT-NlJCr6FQ/edit
 '''
 
 import time
 import sys
 for x in sys.stdin:
     a = map(int,x.split())
     l = a[0]
     r = a[1]
 
 def areDifferent(n):
     s = str(n)
     myDict = {}
     for x in s:
         if x in myDict:
             return False
         myDict.update({x : 1})
     return True
 
 def findNumber(l,r):
     found = False
     for x in range(l+1,r):
         if areDifferent(x):
             print x
             found = True
     if not found:
         print -1
 
 start = time.time()
 findNumber(l, r)
 print ("--- %s seconds ---" % (time.time()-start))
 
 '''
 Prints
 
 -1
 --- 0.000833034515381 seconds ---
 
 for input of l = 98766, r = 100000
 ''' 

And with that, we’re done. To be honest, I was a bit disappointed with this question. A far more interesting version would be printing at least one number in the given range with the given property and increasing the bound on r. This would require a bit more work to solve. As the problem is written, I don’t believe it is possible to optimize the solution.

Thanks for reading! See you tomorrow.

Design a site like this with WordPress.com
Get started