Related
I’ve got a really large file, circa 10m rows, in which I’m trying to populate a column based on conditions on another column via a jsee macro. While it is quite quick for small files, it does take some time for the large file.
//pseudocode
//No sorting on Col1, which can have empty cells too
For all lines in file
IF (cell in Col2 IS empty) AND (cell in Col1 IS NOT empty) AND (cell in Col1 = previous cell in Col1)
THEN cell in Col2 = previous cell in Col2
//jsee code
document.CellMode = true; // Must be cell selection mode
totalLines = document.GetLines();
for( i = 1; i < totalLines; i++ ) {
nref = document.GetCell( i, 1, eeCellIncludeNone );
gsize = document.GetCell( i, 2, eeCellIncludeNone );
if (gsize == "" && nref != "" && nref == document.GetCell( i-1, 1, eeCellIncludeNone ) ) {
document.SetCell( i, 2, document.GetCell( i-1, 2, eeCellIncludeNone ) , eeAutoQuote);
}
}
Input File:
Reference
Group Size
14/12/01819
1
14/12/01820
1
15/01/00191
4
15/01/00191
15/01/00191
15/01/00198
15/01/00292
3
15/01/00292
15/01/00292
15/01/00401
5
15/01/00401
15/01/00402
1
15/01/00403
2
15/01/00403
15/01/00403
15/01/00403
15/01/00404
20/01/01400
1
Output File:
Reference
Group Size
14/12/01819
1
14/12/01820
1
15/01/00191
4
15/01/00191
4
15/01/00191
4
15/01/00198
15/01/00292
3
15/01/00292
3
15/01/00292
3
15/01/00401
5
15/01/00401
5
15/01/00402
1
15/01/00403
2
15/01/00403
2
15/01/00403
2
15/01/00403
2
15/01/00404
20/01/01400
1
Any ideas on how to optimise this and make it run even faster?
I wrote a JavaScript for EmEditor macro for you. You might need to set the correct numbers in the first 2 lines for iColReference and iColGroupSize.
iColReference = 1; // the column index of "Reference"
iColGroupSize = 2; // the column index of "Group Size"
document.CellMode = true; // Must be cell selection mode
sDelimiter = document.Csv.Delimiter; // retrieve the delimiter
nOldHeadingLines = document.HeadingLines; // retrieve old headings
document.HeadingLines = 0; // set No Headings
yBottom = document.GetLines(); // retrieve the number of lines
if( document.GetLine( yBottom ).length == 0 ) { // -1 if the last line is empty
--yBottom;
}
str = document.GetColumn( iColReference, sDelimiter, eeCellIncludeQuotes, 1, yBottom ); // get whole 1st column from top to bottom, separated by TAB
sCol1 = str.split( sDelimiter );
str = document.GetColumn( iColGroupSize, sDelimiter, eeCellIncludeQuotes, 1, yBottom ); // get whole 2nd column from top to bottom, separated by TAB
sCol2 = str.split( sDelimiter );
s1 = "";
s2 = "";
for( i = 0; i < yBottom; ++i ) { // loop through all lines
if( sCol2[i].length != 0 ) {
s1 = sCol1[i];
s2 = sCol2[i];
}
else {
if( s1.length != 0 && sCol1[i] == s1 ) { // same value as previous line, copy s2
if( s2.length != 0 ) {
sCol2[i] = s2;
}
}
else { // different value, empty s1 and s2
s1 = "";
s2 = "";
}
}
}
str = sCol2.join( sDelimiter );
document.SetColumn( iColGroupSize, str, sDelimiter, eeDontQuote ); // set whole 2nd column from top to bottom with the new values
document.HeadingLines = nOldHeadingLines; // restore the original number of headings
To run this, save this code as, for instance, Macro.jsee, and then select this file from Select... in the Macros menu. Finally, select Run Macro.jsee in the Macros menu.
This problem appeared in some regional contest for ICPC.
Given n numbers, you have to remove numbers between i to j such that remaining numbers have least average. You can't remove first and last numbers.
2 <= n <= 10^5
We had a discussion about it, and I am still not able to understand it. Some how this problem can be converted to finding contiguous subarray with maximum sum and then it was solved with binary search in O(nlog n).
I couldn't catch that solution while discussion and now after thinking a lot I am not able to understand that solution.
Link to the original problem in case it's not clear: http://programmingteam.cc.gatech.edu/contest/Mercer14/problems/6.pdf
Here is an approach that I think might work:
Compute the partial average from left for all elements, with and updating average, this can be done in O(N): a_L(i) = (a_L(i-1)*(i-1) + a_L(i))/i
Do the same for the partial averages from the right: a_R(i) = (a_R(i+1)*(N-i) + a_R(i))/(N-i+1)
Find the minimum in of both lists.
If the minimum is in the left partial averages (a_L), look for the minimum right to it in the a_R and the other way around if the minimum is found in a_R.
All parts take O(N). Thus, this would result in an O(N) algorithm. Though, it sounds a little bit simple and I might be missing something.
Edit: The original answer stopped in the middle for both lists, which is insufficient on second thought.
Actually, if the minima overlap, I believe, there is no interval to cut out. Here is a little Python implementation of the algorithm:
grades = [5, 5, 1, 7, 8, 2]
N = len(grades)
glob_avg = float(sum(grades))/float(N)
print('total average: {0}'.format(glob_avg))
avg_L = grades[:]
avg_R = grades[:]
minL = 0
minR = N-1
for i in range(1,N):
avg_L[i] = float(avg_L[i-1]*i + grades[i])/float(i+1)
if avg_L[i] <= avg_L[minL]:
minL = i
avg_R[N-i-1] = float(avg_R[N-i]*i + grades[N-i-1])/float(i+1)
if avg_R[N-i-1] <= avg_R[minR]:
minR = N-i-1
opti_avg = glob_avg
if minL < minR:
first = minL+1
last = minR
opti_avg = (avg_L[first-1]*first + avg_R[last]*(N-last)) / float(N + first - last)
print('')
print('Interval to cut: {0} - {1}'.format(first,last))
for pre in grades[:first]:
print('{0}'.format(pre))
for cut in grades[first:last]:
print('X {0} X'.format(cut))
for post in grades[last:]:
print('{0}'.format(post))
else:
print('NO interval found that would reduce the avg!')
print('')
print('--------------------------------------')
print('minimal avg: {0:0.3f}'.format(opti_avg))
print('--------------------------------------')
I would try checking each value above the global minimum, starting with largest.
You can add to left or right (whichever is largest), as long as the average is above the global average.
Keep a note of any minimums to remaining items.
For each item >= global average
While( average( selected) > global average
If average(un selected items) < best so far
Best so far = selected range
End
Add to selection largest of left and right
End while
End for
Only by finding sequences which are above the average will a minimum for unselected work.
Any item which has been considered as a list can be discounted
Had a go at implementing in Python :-
lst = [ -1, -1,1,-90,1,3,-1,-1,1,2,3,1,2,3,4,1, -1,-1];
First solution - look really at an exhausitve test - allow me to verify correctness.
lbound = 0
ubound = len( lst)
print( ubound );
# from http://math.stackexchange.com/questions/106700/incremental-averageing
def Average( lst, lwr, upr, runAvg = 0, runCnt = 0 ):
cnt = runCnt;
avg = runAvg;
for i in range( lwr, upr ):
cnt = cnt + 1
avg = float(avg) + (float(lst[i]) - avg)/cnt
return (avg, cnt )
bestpos_l = 0
bestpos_u = 0
bestpos_avg = 0
best_cnt = 0
######################################################
# solution in O(N^2) - works always
for i in range( 1, len( lst ) - 1 ):
for j in range( i+1, len(lst ) ):
tpl = Average( lst, 0, i ) # get lower end
res = Average( lst, j, len(lst), tpl[0], tpl[1] )
if (best_cnt == 0 or
(best_cnt < res[1] and res[0] == bestpos_avg ) or
res[0] < bestpos_avg ):
bestpos_l = i
bestpos_u = j
bestpos_avg = res[0]
best_cnt = res[1]
print( "better", i,j, res[0], res[1] )
print( "solution 1", bestpos_l, bestpos_u, bestpos_avg, best_cnt )
This came up with valid answers, but I hadn't appreciated, with the current data set, it doesn't really want the right hand side.
########################################################
# O(N)
#
# Try and minimize left/right sides.
#
# This doesn't work - it knows -90 is really good, but can't decide if to
# ignore -90 from the left, or the right, so does neither.
#
lower = []
upper = []
lower_avg = 0
best_lower = lst[0]
lower_i = 0
best_upper = lst[-1]
upper_avg = 0
upper_i = len(lst) -1
cnt = 0
length = len(lst)
for i in range( 0, length ):
cnt = cnt + 1
lower_avg = float( lower_avg) + ( float(lst[i]) - lower_avg)/cnt
upper_avg = float( upper_avg) + ( float(lst[-(i+1)]) - upper_avg)/cnt
upper.append( upper_avg )
lower.append( lower_avg )
if lower_avg <= best_lower:
best_lower = lower_avg
lower_i = i
if upper_avg <= best_upper:
best_upper = upper_avg
upper_i = (len(lst) - (i+1))
if( lower_i + 1 > upper_i ):
sol2 = Average( lst,0, len(lst ))
else:
sol_tmp = Average( lst,0, lower_i+1 )
sol2 = Average( lst, upper_i, len(lst),sol_tmp[0],sol_tmp[1] )
print( "solution 2", lower_i + 1, upper_i, sol2[0],sol2[1] )
The third solution was what I was trying to explain. My implementation is limited because :-
Couldn't find a good way of finding starting points. I wanted to start from the biggest elements, as they are most likely to reduce the average, but haven't got a good way of finding them.
Wasn't sure about the stability of keeping running-averages. Thought about removing items from the average by un-doing each numbers effect. Wasn't sure how this affected precision.
Was fairly sure that any interval which has been checked, can't have a starting item. That would limit further work, but unsure how best to implement such (keeping O(xx) to a minimum.
Solution 3
#################################
## can we remove first / last? if so, this needs adjusting
def ChooseNext( lst, lwr, upr ):
if lwr > 1 and upr < len(lst) -2:
# both sides available.
if lst[lwr-1] > lst[upr]:
return -1
else:
return 1
elif lwr > 1:
return -1
elif upr < len(lst) -2:
return 1
return 0
# Maximize average of data removed.
glbl_average = Average( lst, 0, len(lst) )
found = False
min_pos = 0
max_pos = 0
best_average = glbl_average[0]
for i in range(1, len(lst ) - 1):
# ignore stuff below average.
if lst[i]> glbl_average[0] or (found == False ):
lwr = i
upr = i+1
cnt = 1 # number for average
avg = lst[i]
tmp = Average( lst, 0, lwr)
lcl = Average( lst, upr, len(lst ), tmp[0], tmp[1] )
if found == False or lcl[0] < best_average:
best_average = lcl[0]
min_pos = lwr
max_pos = upr
found = True
# extend from interval (lwr,upr]
choice = ChooseNext( lst, lwr, upr )
while( choice != 0 ):
if( choice == -1):
new_lwr = lwr -1
new_upr = upr
else:
new_lwr = lwr
new_upr = upr + 1
tmp = Average( lst, 0, new_lwr )
lcl_best = Average( lst, new_upr, len(lst), tmp[0], tmp[1] )
if( lcl_best[0] > glbl_average[0]):
choice = 0
else:
lwr = new_lwr
upr = new_upr
if lcl_best[0] < best_average:
min_pos = lwr
max_pos = upr
best_average = lcl_best[0]
choice = ChooseNext( lst, lwr, upr )
print( "solution 3", min_pos, max_pos, best_average )
I am using MATLAB to find all of the possible combinations of k elements out of n possible elements. I stumbled across this question, but unfortunately it does not solve my problem. Of course, neither does nchoosek as my n is around 100.
Truth is, I don't need all of the possible combinations at the same time. I will explain what I need, as there might be an easier way to achieve the desired result. I have a matrix M of 100 rows and 25 columns.
Think of a submatrix of M as a matrix formed by ALL columns of M and only a subset of the rows. I have a function f that can be applied to any matrix which gives a result of either -1 or 1. For example, you can think of the function as sign(det(A)) where A is any matrix (the exact function is irrelevant for this part of the question).
I want to know what is the biggest number of rows of M for which the submatrix A formed by these rows is such that f(A) = 1. Notice that if f(M) = 1, I am done. However, if this is not the case then I need to start combining rows, starting of all combinations with 99 rows, then taking the ones with 98 rows, and so on.
Up to this point, my implementation had to do with nchoosek which worked when M had only a few rows. However, now that I am working with a relatively bigger dataset, things get stuck. Do any of you guys think of a way to implement this without having to use the above function? Any help would be gladly appreciated.
Here is my minimal working example, it works for small obs_tot but fails when I try to use bigger numbers:
value = -1; obs_tot = 100; n_rows = 25;
mat = randi(obs_tot,n_rows);
while value == -1
posibles = nchoosek(1:obs_tot,i);
[num_tries,num_obs] = size(possibles);
num_try = 1;
while value == 0 && num_try <= num_tries
check = mat(possibles(num_try,:),:);
value = sign(det(check));
num_try = num_try + 1;
end
i = i - 1;
end
obs_used = possibles(num_try-1,:)';
Preamble
As yourself noticed in your question, it would be nice not to have nchoosek to return all possible combinations at the same time but rather to enumerate them one by one in order not to explode memory when n becomes large. So something like:
enumerator = CombinationEnumerator(k, n);
while(enumerator.MoveNext())
currentCombination = enumerator.Current;
...
end
Here is an implementation of such enumerator as a Matlab class. It is based on classic IEnumerator<T> interface in C# / .NET and mimics the subfunction combs in nchoosek (the unrolled way):
%
% PURPOSE:
%
% Enumerates all combinations of length 'k' in a set of length 'n'.
%
% USAGE:
%
% enumerator = CombinaisonEnumerator(k, n);
% while(enumerator.MoveNext())
% currentCombination = enumerator.Current;
% ...
% end
%
%% ---
classdef CombinaisonEnumerator < handle
properties (Dependent) % NB: Matlab R2013b bug => Dependent must be declared before their get/set !
Current; % Gets the current element.
end
methods
function [enumerator] = CombinaisonEnumerator(k, n)
% Creates a new combinations enumerator.
if (~isscalar(n) || (n < 1) || (~isreal(n)) || (n ~= round(n))), error('`n` must be a scalar positive integer.'); end
if (~isscalar(k) || (k < 0) || (~isreal(k)) || (k ~= round(k))), error('`k` must be a scalar positive or null integer.'); end
if (k > n), error('`k` must be less or equal than `n`'); end
enumerator.k = k;
enumerator.n = n;
enumerator.v = 1:n;
enumerator.Reset();
end
function [b] = MoveNext(enumerator)
% Advances the enumerator to the next element of the collection.
if (~enumerator.isOkNext),
b = false; return;
end
if (enumerator.isInVoid)
if (enumerator.k == enumerator.n),
enumerator.isInVoid = false;
enumerator.current = enumerator.v;
elseif (enumerator.k == 1)
enumerator.isInVoid = false;
enumerator.index = 1;
enumerator.current = enumerator.v(enumerator.index);
else
enumerator.isInVoid = false;
enumerator.index = 1;
enumerator.recursion = CombinaisonEnumerator(enumerator.k - 1, enumerator.n - enumerator.index);
enumerator.recursion.v = enumerator.v((enumerator.index + 1):end); % adapt v (todo: should use private constructor)
enumerator.recursion.MoveNext();
enumerator.current = [enumerator.v(enumerator.index) enumerator.recursion.Current];
end
else
if (enumerator.k == enumerator.n),
enumerator.isInVoid = true;
enumerator.isOkNext = false;
elseif (enumerator.k == 1)
enumerator.index = enumerator.index + 1;
if (enumerator.index <= enumerator.n)
enumerator.current = enumerator.v(enumerator.index);
else
enumerator.isInVoid = true;
enumerator.isOkNext = false;
end
else
if (enumerator.recursion.MoveNext())
enumerator.current = [enumerator.v(enumerator.index) enumerator.recursion.Current];
else
enumerator.index = enumerator.index + 1;
if (enumerator.index <= (enumerator.n - enumerator.k + 1))
enumerator.recursion = CombinaisonEnumerator(enumerator.k - 1, enumerator.n - enumerator.index);
enumerator.recursion.v = enumerator.v((enumerator.index + 1):end); % adapt v (todo: should use private constructor)
enumerator.recursion.MoveNext();
enumerator.current = [enumerator.v(enumerator.index) enumerator.recursion.Current];
else
enumerator.isInVoid = true;
enumerator.isOkNext = false;
end
end
end
end
b = enumerator.isOkNext;
end
function [] = Reset(enumerator)
% Sets the enumerator to its initial position, which is before the first element.
enumerator.isInVoid = true;
enumerator.isOkNext = (enumerator.k > 0);
end
function [c] = get.Current(enumerator)
if (enumerator.isInVoid), error('Enumerator is positioned (before/after) the (first/last) element.'); end
c = enumerator.current;
end
end
properties (GetAccess=private, SetAccess=private)
k = [];
n = [];
v = [];
index = [];
recursion = [];
current = [];
isOkNext = false;
isInVoid = true;
end
end
We can test implementation is ok from command window like this:
>> e = CombinaisonEnumerator(3, 6);
>> while(e.MoveNext()), fprintf(1, '%s\n', num2str(e.Current)); end
Which returns as expected the following n!/(k!*(n-k)!) combinations:
1 2 3
1 2 4
1 2 5
1 2 6
1 3 4
1 3 5
1 3 6
1 4 5
1 4 6
1 5 6
2 3 4
2 3 5
2 3 6
2 4 5
2 4 6
2 5 6
3 4 5
3 4 6
3 5 6
4 5 6
Implementation of this enumerator may be further optimized for speed, or by enumerating combinations in an order more appropriate for your case (e.g., test some combinations first rather than others) ... Well, at least it works! :)
Problem solving
Now solving your problem is really easy:
n = 100;
m = 25;
matrix = rand(n, m);
k = n;
cont = true;
while(cont && (k >= 1))
e = CombinationEnumerator(k, n);
while(cont && e.MoveNext());
cont = f(matrix(e.Current(:), :)) ~= 1;
end
if (cont), k = k - 1; end
end
I am failing over and over trying to get this indicator to run quantised with 2 buffers in mql4. After a long time reading I have put 2 extra buffers in to squish it :/ because:
the indicator is sitting between 0.1430-0.1427 at present but doesn't have a fixed top and bottom.
I can't seem to suss it; cool indicator but won't play fair!
#property indicator_separate_window
#property indicator_buffers 4
#property indicator_color1 Lime
#property indicator_color2 Red
#property indicator_color3 CLR_NONE
#property indicator_color4 CLR_NONE
//#property indicator_minimum 0
//#property indicator_maximum 100
extern int P = 13;
extern int T = 3000;
extern double P2 = 0.001;
//int MIN = 0;
//int MAX = 100;
double G[];
double R[];
double B3[];
double B4[];
int init(){
IndicatorBuffers(4);
SetIndexBuffer( 0, G );SetIndexStyle( 0, DRAW_LINE, STYLE_SOLID, 1, Lime );
SetIndexBuffer( 1, R );SetIndexStyle( 1, DRAW_LINE, STYLE_SOLID, 1, Red );
SetIndexBuffer( 2, B3 );SetIndexStyle( 2, DRAW_NONE );
SetIndexBuffer( 3, B4 );SetIndexStyle( 3, DRAW_NONE );
return(0);
}
int start(){
if ( T >= Bars ) T = Bars;
SetIndexDrawBegin( 0, Bars - T + P + 1 );
SetIndexDrawBegin( 1, Bars - T + P + 1 );
SetIndexDrawBegin( 2, Bars - T + P + 1 );
SetIndexDrawBegin( 3, Bars - T + P + 1 );
int Z, C, Opt = IndicatorCounted();
if ( Bars <= 38 ) return(0);
if ( Opt < P ){
for ( Z = 1; Z <= 0; Z++ ) G[T-Z] = 0.0;
for ( Z = 1; Z <= 0; Z++ ) R[T-Z] = 0.0;
}
Z = T - P - 1;
while( Z >= 0 ){
double A, S1, S2;
S1 = 0.0; for ( C = 0; C <= P - 1; C++ ){ S1 = S1 + ( High[Z+C] + Low[Z+C] ) / 2;}
S2 = 0.0; for ( C = 0; C <= P - 1; C++ ){ S2 = S2 + ( ( High[Z+C] + Low[Z+C] ) * ( C+1 ) / 2 );}
A = S1 / S2;
// if ( A < MIN ){ MIN = A;}
// if ( A > MAX ){ MAX = A;}
// A = ( MIN / MAX ) * 100;
G[Z] = A;
if ( Z > 0 ){ R[Z-1] = A;}
Z--;
}
for ( int N = T-P-2; N >= 0; N-- ){
if ( N > 0 ){
if ( G[N-1] > G[N] ){ R[N] = EMPTY_VALUE; continue;}
if ( G[N-1] < G[N] ){ G[N] = R[N]; continue;}
}
B3[0] = G[0] + P2;
B4[0] = G[0] - P2; //forced quantise using 2 extra buffers
}
return(0);
}
Let´s split the task first
0) indicator logic
1) indicator quantisation step
2) indicator performance
MQL4 Custom Indicator programming relies on deeper understanding of underlying MetaTrader4 Terminal platform. Each external Market Event, changing a traded instrument price, is signalled to a lcoalhost by a network delivery of a QUOTE ... message from MetaTrader4 Server. This is aka Tick and it triggers a call to a function originally called start(), in newer New-MQL4.56789 renamed to OnTick().
The below modified MQL4 listing contains remarks for core-logic disambiguation, which must precede all the below listed steps.
1) indicator quantisation step
While the code is still very inefficient ( as per [2] below ) the logic does not include any straight hurdle form having the output quantised to any form thereof { binary | ternary | arbitrary-number-of-states }-quantised system. Whence the indicator core-logic is cleared, the quantisation step is just a trivial conversion from R(1) to I(1).
2) indicator performance
Any Tick arrival may, but need not modify either High[0] or Low[0], which are the only variable parts of the proposed Custom Indicator calculus.
This is the core idea on how to reduce the scope of re-calculations, that the MQL4 code has to realise per tick. In recent versions of MT4, all Custom Indicators share a single thread, the more stress has been put on efficient algorithmisation of Custom Indicators, at these may block the platform's trading decisions on poor, inefficient code-loops and convolution/recursion re-executions.
#property indicator_separate_window
#property indicator_buffers 4
#property indicator_color1 Lime
#property indicator_color2 Red
#property indicator_color3 CLR_NONE
#property indicator_color4 CLR_NONE
extern int P = 13;
extern int T = 3000;
extern double P2 = 0.001;
double G[]; // 0: LINE
double R[]; // 1: LINE
double B3[]; // 2: BUT NEVER PAINTED, NEVER CONSUMED _?_
double B4[]; // 3: BUT NEVER PAINTED, NEVER CONSUMED _?_
int init(){
IndicatorBuffers(4);
SetIndexBuffer( 0, G );SetIndexStyle( 0, DRAW_LINE, STYLE_SOLID, 1, Lime );
SetIndexBuffer( 1, R );SetIndexStyle( 1, DRAW_LINE, STYLE_SOLID, 1, Red );
SetIndexBuffer( 2, B3 );SetIndexStyle( 2, DRAW_NONE );
SetIndexBuffer( 3, B4 );SetIndexStyle( 3, DRAW_NONE );
return(0);
}
int start(){
if ( Bars <= 38 ) return(0); // JIT/RET in case Bars < 39 --^ --^ --^ --^
if ( T >= Bars ) T = Bars; // (TRIM´d) T < Bars .OR. = Bars
int aDrawBegins = Bars - T + P + 1; // ( extern P = 13 ) + 1 + ( Bars - ( extern T = 3000 if T < Bars else Bars ) )
//tIndexDrawBegin( 0, Bars - T + P + 1 ); // PREF: ( reused 4x )
SetIndexDrawBegin( 0, aDrawBegins ); // Draw 14+ last candles -- PREF: why a per tick hard-coded SHIFTING / enforced re-draw?
SetIndexDrawBegin( 1, aDrawBegins ); // Draw 14+ last candles -- PREF: why a per tick hard-coded SHIFTING / enforced re-draw?
SetIndexDrawBegin( 2, aDrawBegins ); // Draw 14+ last candles -- PREF: why a per tick hard-coded SHIFTING / enforced re-draw?
SetIndexDrawBegin( 3, aDrawBegins ); // Draw 14+ last candles -- PREF: why a per tick hard-coded SHIFTING / enforced re-draw?
double A, S1, S2; // auxiliary var for bar-mid-price calculi
int Z; // auxiliary stepper
int Opt = IndicatorCounted(); // Opt ( NEVER RE-USED )
if ( Opt < P ){ // if ( ( extern P = 13 ) > IndicatorCounted() )
// ----------------------- ??? ----------------------------------------------------- NEVER EXEC´d: for( Z = 1++ v/s Z <= 0 )
for ( Z = 1; Z <= 0; Z++ ) G[T-Z] = 0.0; // .STO G[T-Z], 0., BUT NEVER EXEC´d: for( Z = 1++ v/s Z <= 0 )
for ( Z = 1; Z <= 0; Z++ ) R[T-Z] = 0.0; // .STO R[T-Z], 0., BUT NEVER EXEC´d: for( Z = 1++ v/s Z <= 0 )
// ----------------------- ??? ----------------------------------------------------- NEVER EXEC´d: for( Z = 1++ v/s Z <= 0 )
}
Z = T - P - 1; // .STO Z, ( T = Bars (TRIM´d) ) - ( extern P = 13 ) - 1
while( Z >= 0 ){ // .DEC Z
// !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // PERF: very inefficient to RE-calc STATIC ( ( extern P = 13 ) - 1 )-DEEP CONVOLUTIONS per tick !!
S1 = 0.0; for ( int C = 0; C <= P - 1; C++ ){ S1 = S1 + ( High[Z+C] + Low[Z+C] ) / 2; }
S2 = 0.0; for ( int C = 0; C <= P - 1; C++ ){ S2 = S2 + ( ( High[Z+C] + Low[Z+C] ) * ( C+1 ) / 2 );}
// !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // PERF: very inefficient to RE-calc STATIC ( ( extern P = 13 ) - 1 )-DEEP CONVOLUTIONS per tick !!
A = S1 / S2;
G[Z] = A; // .STO G[Z], A if Z >= 0
if ( Z > 0 ){ R[Z-1] = A;} // .STO R[Z-1], A if Z > 0
Z--;
}
for ( int N = T - P - 2; N >= 0; N-- ){ // .STO N, ( T = Bars (TRIM´d) ) - ( extern P = 13 ) - 2
if ( N > 0 ){ // N > 0:
if ( G[N-1] > G[N] ){ R[N] = EMPTY_VALUE; continue;} // .BLNK R[N], EMPTY if G[N-1] > G[N]
if ( G[N-1] < G[N] ){ G[N] = R[N]; continue;} // .SET G[N], R[N] if G[N-1] < G[N]
}
// ?? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // WHY MANY-TIMES RE-ASSIGNED A CONST. VALUE HERE, INSIDE A FOR(){...}-loop body? -------------- ??
B3[0] = G[0] + P2; // .STO B3[0], G[0] + ( extern P2 = 0.001 )
B4[0] = G[0] - P2; // .STO B4[0], G[0] - ( extern P2 = 0.001 )
// forced quantise using 2 extra buffers
// ?? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // WHY MANY-TIMES RE-ASSIGNED A CONST. VALUE HERE, INSIDE A FOR(){...}-loop body? -------------- ??
}
return(0);
}
New-MQL4.56789 syntax
The OnCalculate() function is called only in custom indicators when it's necessary to calculate the indicator values by the Calculate event. This usually happens when a new tick is received for the symbol, for which the indicator is calculated. This indicator is not required to be attached to any price chart of this symbol.
The first rates_total parameter contains the number of bars, available to the indicator for calculation, and corresponds to the number of bars available in the chart.
We should note the connection between the return value of OnCalculate() and the second input parameter prev_calculated. During the OnCalculate() function call, the prev_calculated parameter contains a value returned by OnCalculate() during previous call. This allows for economical algorithms for calculating the custom indicator in order to avoid repeated calculations for those bars that haven't changed since the previous run of this function.
For this, it is usually enough to return the value of the rates_total parameter, which contains the number of bars in the current function call. If since the last call of OnCalculate() the price data has changed (a deeper history downloaded or history blanks filled), the value of the input parameter prev_calculated will be set to zero by the terminal.
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate( const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[]
)
{
// Get the number of bars available now for the current Symbol and chart period
int barsNow = Bars( _Symbol, PERIOD_CURRENT );
// .RET value of prev_calculated for a next call
return( rates_total );
}
I'm using Alberto Santini's solution to this question to get a spiral grid reference based on an items index
Algorithm for iterating over an outward spiral on a discrete 2D grid from the origin
It's not the accepted solution, but it's the best for my needs as it avoids using a loop.
It's working well, but what I want now is to do the inverse. Based on a known x and y coordinate return the index of a location.
This is as a precursor to returning the items surrounding a given location.
Pascal code:
if y * y >= x * x then begin
p := 4 * y * y - y - x;
if y < x then
p := p - 2 * (y - x)
end
else begin
p := 4 * x * x - y - x;
if y < x then
p := p + 2 *(y - x)
end;
Description: Left-upper semi-diagonal (0-4-16-36-64) contains squared layer number (4 * layer^2). External if-statement defines layer and finds (pre-)result for position in corresponding row or column of left-upper semi-plane, and internal if-statement corrects result for mirror position.
I don't know if there is a concise mathematical equation to derive what you want, but I have a solution that computes what you want in O(1) time per query. No loops like you wanted.
My approach :
(i) For any given point (x,y), find the number of points which lie in the square of side length (2*a-1), where a = Max( |x|, |y| ). These are the interior points. i.e, the number of points lying in all spirals NOT including current spiral.
This is nothing but ( 2*a -1 )*( 2*a -1 )
Eg : Consider the following diagram :
y
|
|
16 15 14 13 12
17 4 3 2 11
-- 18 5 0 1 10 --- x
19 6 7 8 9
20 21 22 23 24
|
|
For the point ( 2,1 ), a = 2. The interior points, here are labelled as 0, 1, 2, 3, 4, 5, 6, 7, 8 - The square with edge length 3
(ii) Now compute the points lying on the current spiral. The spiral has 4 "corner" points -
(a) The starting point ( where the current spiral starts )
(b) The point ( a, a )
(c) The point ( -a, a )
(d) The point ( -a, -a )
So, I compute the number of elements lying between each such pair [ i.e, between (a) and (b), (b) and (c), (c) and (d) ], such that all of these fall before the required input point in the spiral sequence. This can be done by simple subtraction of point co-ordinates.
This value, plus the number of interior points will give you the required answer.
I am not sure whether I have explained this very clearly. Do let me know if you require any clarifications or further explanation.
Attached is the JAVA code I wrote to test my logic. I am sorry but it is not very elegant, but it works :P
import java.io.IOException;
import java.util.Scanner;
class Pnt
{
int x, y;
public Pnt( int _x, int _y )
{
x = _x;
y = _y;
}
}
public class Spiral
{
static int interior( Pnt p ) // returns points within interior square of side length MAX( x, y ) - 1
{
int a = Math.max( Math.abs( p.x ), Math.abs( p.y ));
return ( 2*a - 1 )*( 2*a - 1 );
}
static Pnt startPnt( Pnt p ) // first point in that spiral
{
int a = Math.max( Math.abs( p.x ), Math.abs( p.y ));
// last pnt in prev spiral = [ a-1, -( a-1 ) ]
// next pnt = [ a, -( a-1 ) ]
return new Pnt( a, -( a-1 ));
}
static int offSetRow1( Pnt pStart, Pnt p )
{
return ( p.y - pStart.y ) + 1;
}
static int solve( Pnt curr )
{
// check location of curr
// It may lie on 1st row, 2nd row, 3rd or 4th row
int a = Math.max( Math.abs( curr.x ), Math.abs( curr.y ));
int off=0;
int interiorCnt = interior( curr );
Pnt start = startPnt( curr );
if( ( curr.x == a ) && ( curr.y >= start.y ) ) // row 1
{
off = offSetRow1( start, curr );
return off+interiorCnt;
}
if( curr.y == a ) // row 2
{
Pnt start2 = new Pnt( a, a );
int off1 = offSetRow1( start, start2 );
// now add diff in x-coordinates
int off2 = start2.x - curr.x;
off = off1 + off2;
return off+interiorCnt;
}
if( curr.x == -a ) // row 3
{
Pnt start2 = new Pnt( a, a );
int off1 = offSetRow1( start, start2 );
// now add diff in x-coordinates
Pnt start3 = new Pnt( -a, a );
int off2 = start2.x - start3.x;
// now add diff in y co-ordinates
int off3 = start3.y - curr.y;
off = off1 + off2 + off3;
return off+interiorCnt;
}
else // row 4
{
Pnt start2 = new Pnt( a, a );
int off1 = offSetRow1( start, start2 );
// now add diff in x-coordinates
Pnt start3 = new Pnt( -a, a );
int off2 = start2.x - start3.x;
// now add diff in y co-ordinates
int off3 = start3.y - curr.y;
Pnt start4 = new Pnt( -a, -a );
// add diff in x co-ordinates
int off4 = curr.x - start4.x;
off = off1 + off2 + off3 + off4;
return interiorCnt + off;
}
}
public static void main( String[] args ) throws IOException
{
Scanner s = new Scanner( System.in );
while( true )
{
int x = s.nextInt();
int y = s.nextInt();
Pnt curr = new Pnt( x, y );
System.out.println( solve( curr ));
}
}
}
I want to throw in my function since it's a bit more concise than the last solution but more complex than the first.
rather than have the indexes adjacent to each-other, my code opts for loops/layers where the first index of the next loop is always on the same axis.
like so:
23 24 9 10 11 +y
22 8 1 2 12
21 7 0 3 13
20 6 5 4 14
19 18 17 16 15 -y
-x +x
it has set directions and uses the smaller vec2 value as the offset from these NSEW axes
func translate_vector2_to_spiral_index(vec2):
#layer is the ring level the position is on
var layer = max(abs(vec2.x),abs(vec2.y))
if layer == 0:
return 0
#the total interior positions before the edge
var base_index = 0
var i = 0
while i < layer:
base_index += 8 * i
i+=1
var current_layer_total = 8 * i
#non_axis spaces at each corner (not directly any nesw axis)
var non_axis_spaces = (current_layer_total - 4)/4
#direct axes spaces on this layer
var N = 1
var E = N + non_axis_spaces + 1
var S = E + non_axis_spaces + 1
var W = S + non_axis_spaces + 1
var spiral_index = base_index
if abs(vec2.x) > abs(vec2.y):
if vec2.x < 0:
spiral_index+=W
spiral_index += vec2.y
elif vec2.x > 0:
spiral_index+=E
spiral_index -= vec2.y
else:
if vec2.y < 0:
spiral_index+=S
elif vec2.y > 0:
spiral_index+=N
#abs(y) must be equivalent to layers if x is 0
else:
if vec2.y < 0:
spiral_index+=S
spiral_index -= vec2.x
elif vec2.y > 0:
spiral_index
var x = N
x += vec2.x
#if x goes into the negative on the iteration axis (N) it's a subtraction from the layer total
if vec2.x < 0:
x = current_layer_total + 1 + vec2.x
spiral_index += x
else:
if vec2.x < 0:
spiral_index+=W
elif vec2.x > 0:
spiral_index+=E
#abs(x) must be equivalent to layers if y is 0
return spiral_index
there's probably a way to shorten this but i thought to throw this out there.