Friday, June 17, 2016
Now reading...
Thursday, May 19, 2016
on dynamic programming and generating functions
Recently I have had several conversations regarding Dynamic Programming (DP) and related ideas with colleagues, so thought a post here might be in order.
Technique, Applicability, Example
Dynamic programming is a problem solving technique where a problem is broken down into simpler pieces, one or more of which recur. These smaller problems are easier to solve, and once solved, these solutions can be reused and the various parts can then be put together in clever ways to solve the larger problem we started with. The excellent algorithms text by Cormen, Rivest et al [CLRS] covers this in great detail.
Solving problems that fall into this class typically leverages ideas like memoization (remembering the solutions of smaller sub-problems already solved), recurrence relationships, and recursion. A typical oft-cited example is the Fibonacci series of numbers which starts 0, 1, 1, ... and each subsequent term is the sum of two previous ones. From this, the recurrence relationship is easily derived to be:
Fib(n) = Fib(n-1) + Fib(n-2) with Fib(0)=0, and Fib(1)=1. (In this case the recurrence relationship is obvious. For some problems it might not be, and might take some more work to figure out.)
Fibonacci coded as a simple recursion without memoization struggles to calculate even the 40th number in the series. This is an example of a case where the power of algorithms becomes immediately tangible. The code with memoization on the other hand returns the desired answer almost immediately.
# fibonacci series expansion without memoization
def fib2(n):
if n==0: return 0;
elif n==1: return 1;
else: return fib2(n-1)+fib2(n-2);
# fibonacci series computation with memoization
f={0:0,1:1}; # set up the initial dictionary
def fib(n):
if n in f: return f[n]; # if already computed, return it
if n-1 in f and n-2 in f: # if not already computed, but needed terms available,
f[n]=f[n-1]+f[n-2]; # compute needed number, store it, then return it.
return f[n];
return fib(n-1)+fib(n-2); # use the recurrence relationship.
Why does memoization work? Because it remembers calculations that were already done, and instead of recalculating things, just uses these answers again... returning the desired answer almost immediately. For instance, the code without memoization computes Fib(5) as Fib(4)+Fib(3), then compute Fib(4) as Fib(3)+Fib(2), and so on, and the tree of function calls it completes to calculate Fib(3) are repeated all over again in the tree of function calls to compute Fib(4). In contrast, the code with memoization computes each Fibonacci number just once, stores the results in a hash-table, then reuses these as needed.
In other words, the naive algorithm is exponential in complexity terms, while the memo-ized version runs in polynomial time. This is an important distinction. I particularly love this example because this drives home the idea of the importance of algorithmic complexity in directly observable terms (just see how long one takes to compute numbers vs the other), even for an audience of lay-persons.
If at first you don't succeed... try, try again
"Insanity is doing the same thing again and again and expecting different results"
-- Albert Einstein
But wait! Even the code with memoization has some issues, one of which is discussed below. Why does the first attempt to compute fib(3000) fail, but the same call succeed later?
fib(1000);
fib(3000); # gives a RuntimeError: maximum recursion depth exceeded
# but then try
fib(1500);
fib(1800);
fib(2000);
fib(2300);
fib(2600);
fib(2800);
fib(3000); # this works!
This is because in the first instance, the space between the already computed Fibonacci numbers (fib(1000)) to the target Fibonacci number (fib(3000)) is sparse. The number of recursive calls needed to bridge this gap in the memoization table is too large in one go. However, it is possible to work towards that gap by computing more and more numbers in the interim (say a gap of 300-600 each time) till the target is reached. Each of these intermediate calculations further populates the hash table until we can easily compute the desired number.
Does this negate Einstein's famous (and possibly apocryphal) quote? Probably not. Because the second invocation of fib(3000) is not the same as the first. The system is in a different state when it encounters the second call given all the intermediate computations that were completed in the interim.
The Path Forward
Understanding DP is critical to solving many problems asked in software engineering reviews, such as the following. The more impressive candidate however, is generally one that can look at somewhat more complicated problems, de-construct them into simpler sub-problems that are susceptible to DP methods, devise a solution for those, then put those solutions back together to answer the original seemingly intractable problem.
Typical interview questions - most of these can be easily solved using recurrence relations:
Technique, Applicability, Example
Dynamic programming is a problem solving technique where a problem is broken down into simpler pieces, one or more of which recur. These smaller problems are easier to solve, and once solved, these solutions can be reused and the various parts can then be put together in clever ways to solve the larger problem we started with. The excellent algorithms text by Cormen, Rivest et al [CLRS] covers this in great detail.
Solving problems that fall into this class typically leverages ideas like memoization (remembering the solutions of smaller sub-problems already solved), recurrence relationships, and recursion. A typical oft-cited example is the Fibonacci series of numbers which starts 0, 1, 1, ... and each subsequent term is the sum of two previous ones. From this, the recurrence relationship is easily derived to be:
Fib(n) = Fib(n-1) + Fib(n-2) with Fib(0)=0, and Fib(1)=1. (In this case the recurrence relationship is obvious. For some problems it might not be, and might take some more work to figure out.)
Fibonacci coded as a simple recursion without memoization struggles to calculate even the 40th number in the series. This is an example of a case where the power of algorithms becomes immediately tangible. The code with memoization on the other hand returns the desired answer almost immediately.
# fibonacci series expansion without memoization
def fib2(n):
if n==0: return 0;
elif n==1: return 1;
else: return fib2(n-1)+fib2(n-2);
f={0:0,1:1}; # set up the initial dictionary
def fib(n):
if n in f: return f[n]; # if already computed, return it
if n-1 in f and n-2 in f: # if not already computed, but needed terms available,
f[n]=f[n-1]+f[n-2]; # compute needed number, store it, then return it.
return f[n];
return fib(n-1)+fib(n-2); # use the recurrence relationship.
Why does memoization work? Because it remembers calculations that were already done, and instead of recalculating things, just uses these answers again... returning the desired answer almost immediately. For instance, the code without memoization computes Fib(5) as Fib(4)+Fib(3), then compute Fib(4) as Fib(3)+Fib(2), and so on, and the tree of function calls it completes to calculate Fib(3) are repeated all over again in the tree of function calls to compute Fib(4). In contrast, the code with memoization computes each Fibonacci number just once, stores the results in a hash-table, then reuses these as needed.
In other words, the naive algorithm is exponential in complexity terms, while the memo-ized version runs in polynomial time. This is an important distinction. I particularly love this example because this drives home the idea of the importance of algorithmic complexity in directly observable terms (just see how long one takes to compute numbers vs the other), even for an audience of lay-persons.
If at first you don't succeed... try, try again
"Insanity is doing the same thing again and again and expecting different results"
-- Albert Einstein
But wait! Even the code with memoization has some issues, one of which is discussed below. Why does the first attempt to compute fib(3000) fail, but the same call succeed later?
fib(1000);
fib(3000); # gives a RuntimeError: maximum recursion depth exceeded
# but then try
fib(1500);
fib(1800);
fib(2000);
fib(2300);
fib(2600);
fib(2800);
fib(3000); # this works!
This is because in the first instance, the space between the already computed Fibonacci numbers (fib(1000)) to the target Fibonacci number (fib(3000)) is sparse. The number of recursive calls needed to bridge this gap in the memoization table is too large in one go. However, it is possible to work towards that gap by computing more and more numbers in the interim (say a gap of 300-600 each time) till the target is reached. Each of these intermediate calculations further populates the hash table until we can easily compute the desired number.
Does this negate Einstein's famous (and possibly apocryphal) quote? Probably not. Because the second invocation of fib(3000) is not the same as the first. The system is in a different state when it encounters the second call given all the intermediate computations that were completed in the interim.
The Path Forward
Understanding DP is critical to solving many problems asked in software engineering reviews, such as the following. The more impressive candidate however, is generally one that can look at somewhat more complicated problems, de-construct them into simpler sub-problems that are susceptible to DP methods, devise a solution for those, then put those solutions back together to answer the original seemingly intractable problem.
Typical interview questions - most of these can be easily solved using recurrence relations:
- Devise the most efficient means of finding the largest internally increasing or decreasing sub-sequence given a list of numbers. What are they? How many exist? How long are they?
- Demonstrate how to find the largest common sub-sequence given two sets of numbers or letters. A good interviewer will ask you both to indicate the size of the largest sub-sequence as well as give him the list of all sub-sequences that meet his criteria.
- In how many ways can you make change for $x, using coins with various denominations.
- In how many ways can you partition a number K using positive integers <=K? e.g. 5=1+4=1+1+1+3=2+2+1 etc.,
Sunday, March 20, 2016
Machine Learning study questions
This is a very basic set of questions in Machine Learning. This post is incomplete, more questions to be added over time.
Theory
Applications
Implementation
...
Theory
- What is bias? variance? What is the bias-variance trade-off?
- What is meant by the term "the curse of dimensionality"?
- Explain over-fitting with an example
- Explain Bayes theorem with an example
- What is a V-C bound?
- What is the Hoeffding inequality? What does it mean?
- What are parametric, semi-parametric, and non-parametric methods? Give an example of each.
- What is supervised learning? unsupervised learning? reinforcement learning? online learning?
Methods
- Briefly describe the perceptron model and how it works
- What would you look at as you design a linear regression and read its output?
- What is multicollinearity? Heteroskedasticity?
- What is a confidence interval? t-statistic? p-value?
- What is Laplacian smoothing? When and how would you use it?
- What is a logistic regression? Where would you use it? How do you read its output?
- Why is logistic regression a useful technique? Which kinds of data distributions does it work best with?
- What is a generalized additive model?
- What is gradient descent? Explain stochastic gradient descent. What assumptions does it make?
- How would you influence the rate of convergence of stochastic gradient descent?
- What is Hill Climbing? Give an example of where you would use this.
- What is cross-validation? Describe two different ways of performing this.
- Describe three distinct ways of dealing with data sets that have more features than samples available.
- What are neural networks? How do they work? What are the different kinds of neural nets?
- What is deep learning?
- Describe how one might design the structure of a neural net to solve a problem.
- What are the different techniques of optimizing weights to reflect the learning a neural network undergoes as it is trained with a data set? (e.g. genetic algorithms, particle swarm optimization, back propagation)
- What is maximum likelihood estimation? How would you use it?
- Describe a naive Bayes model with an example.
- What is a Probabilistic Graphical Model (PGM)? Where might it be used?
- What are kernel methods? Give examples of their use (e.g. kernel smoothing, support vector machines)
- What are genetic algorithms? Illustrate with an example where and how these can be used.
- What are support vector machines (SVMs)? How can they be built and used?
- What is a self organizing map? How does it work? What is vector quantization?
Applications
- You are given a million points in 10 dimensional space. I have a hypothesis that these points are clustered in 8 groups. I want to test this hypothesis. What methods would you use and why?
- What is the complexity of the method you would use for the above?
- Perform a relative computational complexity analysis of K-means clustering vs. Hierarchical Clustering. Does it make a difference to your analysis if you do agglomerative hierarchical clustering vs divisive hierarchical clustering?
- The clusters you see when you run your algorithms on my data are elongated hyper-ellipsoids. What does this tell you about the data? What would you do in this situation to make inferencing easier?
Implementation
...
Wednesday, February 3, 2016
business growth drivers
What drives business growth?
If you're in any business, any industry, or even choosing one to be in, it makes sense to think through this to see the impact of your choice on your future... particularly so if you have already taken steps to future-proof your skill-set to give you freedom to explore multiple possible evolutions in the path forward.
A model like Porter's five forces is often quite useful in performing these kinds of analyses, but as with everything else, your analysis is based on the best information you have when you build your model, and as either the world changes and more information comes to light, or as you uncover more information about your domain of interest through your own study or research, it makes sense to revisit your assumptions.
In this post we look at some common business models prevalent today and study business growth drivers in each case.
Example: "Platform services"
This is an example of a service where network effects play a major role in driving growth. Take for example an Internet video hosting service. You need to achieve critical mass both in terms of people viewing content as well as with those generating the content being viewed i.e. you are building up a platform. When you have more content, you attract people with different tastes to come in to view it, and as more people view the content, you have more people who want to generate the content (who want the viewers' eyeballs), and you get a "virtuous cycle". Once you have critical mass, growth picks up suddenly, then takes off.
Similar arguments hold for other such services - new credit-card launch (merchants and users), new social media platform (users - content consumers, and users - content generators), Internet marketplaces (merchants and customers). Key to growth in each of these cases is achieving critical mass as a first entrant, before competitors can grow. Once you have a lock, further growth should be easy, and your company will grow to be a behemoth... if you don't make costly mis-steps.
So what helps growth here?
Now let's look at a slightly different business model.
Example: Internet Search
Here the business drivers are quite different. The market is characterized by:
If you're in any business, any industry, or even choosing one to be in, it makes sense to think through this to see the impact of your choice on your future... particularly so if you have already taken steps to future-proof your skill-set to give you freedom to explore multiple possible evolutions in the path forward.
A model like Porter's five forces is often quite useful in performing these kinds of analyses, but as with everything else, your analysis is based on the best information you have when you build your model, and as either the world changes and more information comes to light, or as you uncover more information about your domain of interest through your own study or research, it makes sense to revisit your assumptions.
In this post we look at some common business models prevalent today and study business growth drivers in each case.
Example: "Platform services"
This is an example of a service where network effects play a major role in driving growth. Take for example an Internet video hosting service. You need to achieve critical mass both in terms of people viewing content as well as with those generating the content being viewed i.e. you are building up a platform. When you have more content, you attract people with different tastes to come in to view it, and as more people view the content, you have more people who want to generate the content (who want the viewers' eyeballs), and you get a "virtuous cycle". Once you have critical mass, growth picks up suddenly, then takes off.
Similar arguments hold for other such services - new credit-card launch (merchants and users), new social media platform (users - content consumers, and users - content generators), Internet marketplaces (merchants and customers). Key to growth in each of these cases is achieving critical mass as a first entrant, before competitors can grow. Once you have a lock, further growth should be easy, and your company will grow to be a behemoth... if you don't make costly mis-steps.
So what helps growth here?
- critical mass - lots of people like your service, and lots more are joining every day, on both sides of the platform - both the content creators and the consumers
- sticky service - people like your service, and it is not easy for competitors to build something comparable
- high switching costs - if people "join" the service together with their friends, and contribute content to the platform or form relationships on the platform, they will be less willing to leave as individuals, losing the network they have built there
- "protected" innovation - if you have patents on new technology that makes your service attractive, differentiates it from anything else available when it is first launched, and is hard for competitors to replicate, you have time to build your "moat" which will raise switching costs over time. That, and the investment (see (6) on monetization below) to keep the business at the leading edge contribute to faster growth
- barriers to entry - ideally you want a situation where you can enter new business domains at low cost (say as a first mover) but where, as time progresses, barriers to entry rise exponentially for competitors who want to clone your success from doing the same.
- easy and early monetization - many of the examples cited (e.g. video hosting, social media) in support of this model utilize advertising to pay the bills. Others charge users outright (e.g. credit card companies charge users an annual fee, and merchants a processing fee per transaction for the convenience). Yet others use differentiated pricing for different levels of service (e.g. reduced delivery charges) or force higher discounts on sellers for access to a high-volume sales platform.
- First, you have to give people a reason to switch. Is there something compelling you can offer, that users both want badly, cannot get from Facebook (e.g. you own patents on the technology), while giving them all the features they are already accustomed to? Alternatively, find "pain points" on the existing service, then build your platform to specifically address those, then advertise these differences every chance you get.
- Second, you have to give them the means to move from the platform they are currently on to your platform, seamlessly. How would you do this? If user content on the social media platform belongs to the user, not the platform provider, you can provide the user with tools to move her content to your platform, while simultaneously emailing or contacting her network on the old platform about the move. The tools would of course tout your new platform's differentiated capabilities to the user's network. If you can pull down some small fraction of the user's network with each switch, you can slowly work forward towards critical mass in your user population. It goes without saying that the tool we speak of should work completely seamlessly and without any hitches if we want users to switch.
- Third, you want to capture as much of the new subscriber market as you can. People not already on the existing leading platform, but wanting to join. And those who joined the existing platform but are disenchanted with it.
- And finally, you want to identify high impact users - these are the ones that are most connected on the existing platform - and get them to switch, offering whatever incentives you can. Of course, as a competitor you do not have access to any user's connectivity graph, but networks like LinkedIn for instance routinely indicate the number of connections or friends a person has, and this helps identify users that are a. better connected, and b. more active on the existing platform, and these are valuable to get to switch. If the platform has service (e.g. games etc) providers, use the same approach to get them to sign on to your platform inclusively at first, then exclusively as you gain mind-share.
- Also, use whatever opportunities present themselves to push your agenda. A network failure on the old service with lots of complaints on service inaccessibility? Advertise heavily so people think of your service then. Flaunt early adopters' testimonials and the differentiated nature of your offering every chance you get. Create a buzz. Get noticed. Win market-share.
Now let's look at a slightly different business model.
Example: Internet Search
Here the business drivers are quite different. The market is characterized by:
- several direct substitutes - google, bing, duck duck go, come to mind at once
- low switching costs - all a dissatisfied user has to do is click another link
- high barriers to entry - sure, writing a search engine is not that difficult, but writing a competitive one that can take on the big boys on their home turf is strictly non-trivial
- easy interoperability - all search features common to various engines are table-stakes. 100% interoperability in terms of being able to search for various kinds of pages or "toolbar API" capabilities are expected.
- established underlying standards - some searches run better in "home" browsers than in others (e.g. bing in IE). Sometimes one might even argue that the search engine pegged by default into a browser's download settings has an advantage out of the gate, though savvy users will of course change these settings to best choose their tastes.
- expectations of feature parity - also covered under (4) above. And when the expected nice features one is used to are not available, this causes people to switch. e.g. google identifies and tags pdf documents as pdfs. The quality of search results is arguably better. There is a side panel with a summary story of your search terms. All these things keep users coming back.
- strong patent moats for innovation - the search wars are won or lost on algorithms these days. What machine learning techniques underpin one search engine vs another, making one better or worse? Does the engine unobtrusively store your search history and use that to set a context for your future searches?
- scope for some meaningful differentiation - how do you monetize it? ad-revenue? automated reverse dutch auction bidding for keywords? Something else? Is your technology patented? A cash cow can not only take care of your search business, but also fund your other innovation.
- grab those eyeballs on all possible screens: feel PC screens are saturated with your users? move to mobile. integrate their search experience across screens. let users bookmark searches, ... the list goes on.
So how do you position yourself to succeed? The marketplace here is littered with the carcasses of even some erstwhile successful firms. Alta Vista, Inktomi/ Hotbot, Yahoo!, the list goes on. Once again today there is a single strong firm in the lead, but that position has been held before by others that have since become obsolete. So what makes one firm more able to entrench itself and grow, while earlier leaders wither away?
One could argue that the above factors coupled with constant innovation and large smart indexing of the ever expanding Internet is key.
Subscribe to:
Posts (Atom)