Dynamic programming - recursive implementation

First of all, I want to come to my senses and say that the next question is about school, so do not be too hard on me :)

I have a bit of a problem modeling the optimization problem in Matlab using a recursive algorithm (which is a requirement).

Definition of the problem:

Determine the number of fish that you need to catch every year, given the time window of 10 years, knowing that there are currently 10,000 fish in the lake in the lake, year 1, the fish growth rate is the number of fish present in the lake at the beginning each year + 20% .

Let x be the number of fish to be caught, $ 5 is the price of each fish and the cost of fishing:

0.4x + 100 if x is <= 5000; 0.3x + 5000 if 5000 <= x <= 10000; 0.2x + 10000 if x > 10000; 

determine the amount of fish you need to catch every year for 10 years to maximize profits.

Future profits are depreciated 0.2 times per year, which means that earning $ 1 per year 1 is the same as $ 0.8 per year 2, etc.

I have currently defined the following objective function:

 x -> quantity of fish to catch b-> quantity of fish availavable in the beginning of year i c(x,b) -> cost of catching x fish with b fishes available f_i(b) = max {(5x - c(x,b)) + 0.8 * f_i+1((b - x) * 1.2)} 

How can I implement this in matlab?

This is what I still have:

Main file

 clear; global M Kdep Cost RecursiveProfit ValorF prop Kdep=[10; 20; 30; 40; 50; 60; 70; 80; 90; 100]; %max nr of fish in the lake at the beginning of each year, 10 years, in thousands. Growth factor = 20% M=1000; %Cost and Profit of selling i fishes given that there are j at the beginning of the year for i = 1:50 for j = 1:11 Cost(i,j) = 0.2 * i + 10; RecursiveProfit(i,j) = 5 * i - Cost(i, j); end end for i = 1:10 for j = 1:10 Cost(i,j) = 0.3 * i + 5; RecursiveProfit(i,j) = 5 * i - Cost(i, j); end end for i = 1:5 for j = 1:5 Cost(i,j) = 0.4 * i + 0.1; RecursiveProfit(i,j) = 5 * i - Cost(i, j); end end %prop = 1 : 10; ValorF = -M * ones(10, 50); for a = 1:5 ValorF(10, a) = 5 * a - (0.4 * a + 1); %On Year 10, if there are <= a thousand fishes in the lake ... prop(10, a) = a; end for b = 6:10 ValorF(10, b) = 5 * b - (0.3 * b + 5); %On Year 10, if there are 6 <= a <= 10 thousand fishes in the lake ... prop(10, b) = b; end for c = 10:41 ValorF(10, c) = 5 * c - (0.2 * c + 10); prop(10, c) = c; end MaxProfit = RecursiveProfit(1, 10) k1 = prop(1,10) kant=k1; y = 6 - Cost(kant,10); for q=2:10 if(kant == 0) kant = kant + 1; end kq=prop(q,y) kant=kq; y = y - Cost(kant,q); end %for i 

Function

 function y=RecursiveProfit(j,x) global M Kdep Cost Prof ValorF prop y=ValorF(j,x); if y~= -M return end %if auxMax=-M; decision=0; for k=1:Kdep(j) if Prof(k,j) <= xk aux=Prof(k,j)+RecursiveProfit(j+1, (x - k)); if auxMax < aux auxMax=aux; decision=k; end %if aux else break end %if Cost end %for k ValorF(j,x)=auxMax; prop(j,x)=decision; y=auxMax; 

This is calculated only for the case when the year is 10 and b = 10 (value in thousands). This is the same problem that is described as the "problem with profit reduction" in the book

Any help you can give me would be greatly appreciated.

EDIT 1: I'm really stuck here guys. If you could help me implement this in Java, I would try porting it to Matlab.

EDIT 2: I edited the code to the latest version. Now i get

"The maximum recursion limit of 500 has been reached."

Can you help me?

EDIT 3: I managed to get it to work, but it only returns 0.

EDIT 4: Updated code. Now i get

Attempt to access prop (2,0); the index must be a positive integer or logical.

Error in the main (line 66) kq = prop (q, y)

+6
source share
2 answers
 function gofishing(numoffishes,years) growFactor=1.2; %index shift, index 1 : 0 fishes earn{1}=-inf(numoffishes+1,1); %index shift, index 1 : 0 fishes earn{1}(numoffishes+1)=0; %previous: backpointer to find path of solution. previous{1}=nan; %index shift, index 1 : 0 fishes vcosts = zeros(1,ceil(numoffishes*growFactor^years)); for idx=1:numel(vcosts) vcosts(idx)=costs(idx-1); end for step = 1:years*2 fprintf('step %d\n',step); if mod(step,2)==1; %do fish grow step earn{step+1}=-inf(floor(numel(earn{step})*1.2)-1,1); previous{step+1}=nan(floor(numel(earn{step})*1.2)-1,1); for fishes=0:numel(earn{step})-1 grownfishes=floor(fishes*1.2); earn{step+1}(grownfishes+1)=earn{step}(fishes+1); previous{step+1}(grownfishes+1)=fishes; end else %do fishing step earn{step+1}=-inf(size(earn{step})); previous{step+1}=nan(size(earn{step})); for fishes=0:numel(earn{step})-1 if isinf(earn{step}(fishes+1)) %earn is -inf, nothing to do continue; end possibleToFish=fishes:-1:0; %calculate earn for possible amounts to fish options=((vrevenue(possibleToFish)-vcosts(possibleToFish+1))*0.8^(step/2-1)+earn{step}(fishes+1))'; %append -inf for not existing options options=[options;-Inf(numel(earn{step+1})-numel(options),1)]; %found better option: better=earn{step+1}<options; earn{step+1}(better)=options(better); previous{step+1}(better)=fishes; end end end [~,fc]=max(earn{end}); fc=fc-1; fprintf('ending with %d fishes and a earn of %d\n',fc,earn{end}(fc+1)); for step=(years*2):-1:2 fc=previous{step}(fc+1); fprintf('fish count %d\n',fc'); end end function c=costs(x) if (x<=5000) c=0.4*x + 100; return end if (x <= 10000) c=0.3*x + 5000; return end c=0.2*x + 10000; return end function c=vrevenue(x) c=5.*x; end 

After reading my solution again, I have some ideas for improving performance:

  • Instead of indexing vcosts with a vector (possibleToFish), directly use indexes for indexes.
  • Predefine parameters / container in one step.

For 10000, it starts at an acceptable time (about 5 minutes), for big data, I would recommend updating.

+1
source

A relatively clean and classic implementation of the solution using dynamic programming, which should provide a guaranteed optimal solution (in the absence of errors):

 function [max_profit, Ncatch] = fish(nstart, nyear, grow_rate, dep_rate) % return the maximum possible profit max_prof and a vector Ncatch which says % how many fish to catch for each year global cache if isempty(cache) % init_cache maxfish = ceil(nstart * grow_rate^nyear); % allocate abundant cache space for dynamic programming cache.profit = nan(maxfish+1, nyear); cache.ncatch = cell(maxfish+1, nyear); % function calls are expensive, cache calls to revenue and cost cache.netprofit = arrayfun(@(i) revenue(i) - cost(i), 0:maxfish); cache.grow_rate = grow_rate; cache.dep_rate = dep_rate; end if ~isnan(cache.profit(nstart+1, nyear)) % found in cache max_profit = cache.profit(nstart+1, nyear); Ncatch = cache.ncatch{nstart+1, nyear}; %assert(cache.grow_rate == grow_rate, 'clear cache first!') %assert(cache.dep_rate == dep_rate, 'clear cache first!') return end max_profit = -inf; if nyear == 1 % base case to stop recursion % simply get largest profit, future be damned [max_profit, imx] = max(cache.netprofit(1:nstart+1)); Ncatch = [imx - 1]; else % recursive part for ncatch = 0:nstart % try all possible actions nleft = nstart - ncatch; % catch nleft = floor(grow_rate * nleft); % reproduce % recursive step, uses optimal profit for 1 year less [rec_profit, rec_Ncatch] = fish(nleft, nyear - 1, grow_rate, dep_rate); profit = cache.netprofit(ncatch + 1) + dep_rate * rec_profit; if profit > max_profit max_profit = profit; Ncatch = [ncatch, rec_Ncatch]; end end end % store result in cache cache.profit(nstart+1, nyear) = max_profit; cache.ncatch{nstart+1, nyear} = Ncatch; end function c = cost(x) if (x <= 5000) c = 0.4 * x + 100; return end if (x <= 10000) c = 0.3 * x + 5000; return end c = 0.2 * x + 10000; end function r = revenue(x) r = 5 .* x; end 

The only problem is that it is rather slow, I think the runtime is something like O(nyear * (nstart*grow_rate^(nyear-1))^2) . This is polynomial time (?), With the exception of exponential grow_rate. (Note that caching is even better than brute force solution, which will be O((nstart*grow_rate^(nyear-1))^nyear) , which exponentially in nyear .) The quadratic dependence on nstart makes it too run nstart = 10000 slowly (maybe about a day), but for nstart = 200 it still works at an acceptable time. Quick test:

 clear global % clear the cache global cache nyear = 10; nstart = 200; grow_rate = 1.2; dep_rate = 0.80; tic; [profit, ncatch] = fish(nstart, nyear, grow_rate, dep_rate); toc; nfish = nstart; for i = 1:nyear nnew = floor(grow_rate * (nfish - ncatch(i))); prof = cache.netprofit(ncatch(i) + 1); dep_prof = prof * (dep_rate)^(i-1); fprintf('year %d: start %d, catch %d, left %d, profit %.1f, dep. prof %.1f\n', ... i, nfish, ncatch(i), nnew, prof, dep_prof); nfish = nnew; end fprintf('Total profit: %.1f\n', profit); 

with the result

 >> test_fish Elapsed time is 58.591110 seconds. year 1: start 200, catch 200, left 0, profit 820.0, dep. prof 820.0 year 2: start 0, catch 0, left 0, profit -100.0, dep. prof -80.0 year 3: start 0, catch 0, left 0, profit -100.0, dep. prof -64.0 year 4: start 0, catch 0, left 0, profit -100.0, dep. prof -51.2 year 5: start 0, catch 0, left 0, profit -100.0, dep. prof -41.0 year 6: start 0, catch 0, left 0, profit -100.0, dep. prof -32.8 year 7: start 0, catch 0, left 0, profit -100.0, dep. prof -26.2 year 8: start 0, catch 0, left 0, profit -100.0, dep. prof -21.0 year 9: start 0, catch 0, left 0, profit -100.0, dep. prof -16.8 year 10: start 0, catch 0, left 0, profit -100.0, dep. prof -13.4 Total profit: 473.6 

Thus, the depreciation rate is so high that the best solution is to clean the entire lake in the first year. The decrease in this depreciated speed is a little ( dep_rate = 0.84; ), the situation turns around:

 >> test_fish Elapsed time is 55.872516 seconds. year 1: start 200, catch 0, left 240, profit -100.0, dep. prof -100.0 year 2: start 240, catch 0, left 288, profit -100.0, dep. prof -84.0 year 3: start 288, catch 3, left 342, profit -86.2, dep. prof -60.8 year 4: start 342, catch 2, left 408, profit -90.8, dep. prof -53.8 year 5: start 408, catch 3, left 486, profit -86.2, dep. prof -42.9 year 6: start 486, catch 1, left 582, profit -95.4, dep. prof -39.9 year 7: start 582, catch 2, left 696, profit -90.8, dep. prof -31.9 year 8: start 696, catch 1, left 834, profit -95.4, dep. prof -28.2 year 9: start 834, catch 4, left 996, profit -81.6, dep. prof -20.2 year 10: start 996, catch 996, left 0, profit 4481.6, dep. prof 933.1 Total profit: 471.4 

In this case, the growth rate is higher than the devaluation rate, so the best solution is to allow the fish to breed for as long as possible and release the lake last year. Interestingly, the solution suggests catching a few fish in intermediate years. I assume that this extra fish will not change the rounding in nleft = floor(grow_rate * nleft) , so you will gain a little by catching them earlier. Playing a little with dep_rate , this seems like a pretty critical all-or-nothing situation: if grow_rate is tall enough, you catch everything last year. If it is too low, you catch everything in the first year.

0
source

Source: https://habr.com/ru/post/957286/


All Articles