Longest arithmetic and geometric progression sequence error - delphi
I need input sequence of Integer number and find the longest arithmetic and geometric progression sequence. I had wrote this code( I must use Delphi 7)
program arithmeticAndGeometricProgression;
{ 203. In specifeied sequence of integer numbers find the longest sequence, which is
arithmetic or geometric progression. }
{$APPTYPE CONSOLE}
uses
SysUtils;
var
sequence, longArithmSequ, longGeomSequ: Array of Integer;
curArithmSequ, curGeomSequ: Array of Integer; // Current progress
q, q1: Double;
d1, d: Double;
i, k: Integer;
begin
i := 0;
d := 0;
k := 0;
d1 := 0;
Repeat
SetLength(sequence, i + 1);
// Make room for another item in the array
try
read(sequence[i]);
except // If the input character is not an integer interrupt cycle
Break;
end;
inc(i);
Until False;
k := 0;
curArithmSequ := NIL;
curGeomSequ := NIL;
longArithmSequ := NIL;
longGeomSequ := NIL;
d1 := sequence[1] - sequence[0];
q1 := sequence[1] / sequence[0];
i := 1;
repeat
d := d1;
q := q1;
d1 := sequence[i] - sequence[i - 1];
q1 := sequence[i] / sequence[i - 1];
if d = d1 then
begin
SetLength(curArithmSequ, Length(curArithmSequ) + 1);
curArithmSequ[Length(curArithmSequ) - 1] := sequence[i];
end;
if q = q1 then
begin
SetLength(curGeomSequ, Length(curGeomSequ) + 1);
curGeomSequ[Length(curGeomSequ) - 1] := sequence[i];
end;
if Length(curArithmSequ) > Length(longArithmSequ) then
begin
longArithmSequ := NIL;
SetLength(longArithmSequ, Length(curArithmSequ));
for k := 0 to Length(curArithmSequ) - 1 do
longArithmSequ[k] := curArithmSequ[k];
end;
if Length(curGeomSequ) > Length(longGeomSequ) then
begin
longGeomSequ := NIL;
SetLength(longGeomSequ, Length(curGeomSequ));
for k := 0 to Length(curGeomSequ) - 1 do
longGeomSequ[k] := curGeomSequ[k];
end;
if d <> d1 then
curArithmSequ := NIL;
if q <> q1 then
curGeomSequ := NIL;
inc(i);
Until i >= Length(sequence) - 1;
writeLn('The Longest Arithmetic Progression');
for k := 0 to Length(longArithmSequ) - 1 do
Write(longArithmSequ[k], ' ');
writeLn('The Longest Geometric Progression');
for k := 0 to Length(longGeomSequ) - 1 do
Write(longGeomSequ[k], ' ');
Readln(k);
end.
I have such question:
Why it can't print first 1-2 members of arithmetic progression
Why it always print '2' as geometric progression
Is there monkey-style code in my programm?
Please mention to me where are my mistakes.
Updated:
You need to change the logic inside the repeat loop in this way:
if d = d1 then
begin
if (Length(curArithmSequ) = 0) then
begin
if (i > 1) then
SetLength(curArithmSequ,3)
else
SetLength(curArithmSequ,2);
end
else
SetLength(curArithmSequ,Length(curArithmSequ)+1);
for k := 0 to Length(curArithmSequ) - 1 do
curArithmSequ[k] := sequence[i - (Length(curArithmSequ) - k - 1)];
end
else
SetLength(curArithmSequ,0);
if q = q1 then
begin
if (Length(curGeomSequ) = 0) then
begin
if (i > 1) then
SetLength(curGeomSequ,3)
else
SetLength(curGeomSequ,2);
end
else
SetLength(curGeomSequ,Length(curGeomSequ)+1);
for k := 0 to Length(curGeomSequ) - 1 do
curGeomSequ[k] := sequence[i - (Length(curGeomSequ) - k - 1)];
end
else
SetLength(curGeomSequ,0);
An input sequence of:
2,6,18,54 gives LAP=2,6 and LGP=2,6,18,54
while an input sequence of:
1,3,5,7,9 gives: LAP=1,3,5,7,9 and LGP=1,3
And a sequence of
5,4,78,2,3,4,5,6,18,54,16 gives LAP=2,3,4,5,6 and LGP=6,18,54
Here is my complete test (see comments below):
program arithmeticAndGeometricProgression;
{ 203. In specified sequence of integer numbers find the longest sequence, which is
arithmetic or geometric progression. }
{$APPTYPE CONSOLE}
uses
SysUtils;
Type
TIntArr = array of integer;
TValidationProc = function( const sequence : array of integer) : Boolean;
function IsValidArithmeticSequence( const sequence : array of integer) : Boolean;
begin
Result :=
(Length(sequence) = 2) // Always true for a sequence of 2 values
or
// An arithmetic sequence is defined by: a,a+n,a+2*n, ...
// This gives: a+n - a = a+2*n - (a+n)
// s[1] - s[0] = s[2] - s[1] <=> 2*s[1] = s[2] + s[0]
(2*sequence[1] = (Sequence[2] + sequence[0]));
end;
function IsValidGeometricSequence( const sequence : array of integer) : Boolean;
var
i,zeroCnt : Integer;
begin
// If a zero exists in a sequence all members must be zero
zeroCnt := 0;
for i := 0 to High(sequence) do
if (sequence[i] = 0) then
Inc(zeroCnt);
if (Length(sequence) = 2) then
Result := (zeroCnt in [0,2])
else
// A geometric sequence is defined by: a*r^0,a*r^1,a*r^2 + ... ; r <> 0
// By comparing sequence[i]*sequence[i-2] with Sqr(sequence[i-1])
// i.e. a*(a*r^2) with Sqr(a*r) we can establish a valid geometric sequence
Result := (zeroCnt in [0,3]) and (Sqr(sequence[1]) = sequence[0]*Sequence[2]);
end;
procedure AddSequence( var arr : TIntArr; sequence : array of Integer);
var
i,len : Integer;
begin
len := Length(arr);
SetLength(arr,len + Length(sequence));
for i := 0 to High(sequence) do
arr[len+i] := sequence[i];
end;
function GetLongestSequence( IsValidSequence : TValidationProc;
const inputArr : array of integer) : TIntArr;
var
i : Integer;
currentSequence : TIntArr;
begin
SetLength(Result,0);
SetLength(currentSequence,0);
if (Length(inputArr) <= 1)
then Exit;
for i := 1 to Length(inputArr)-1 do begin
if (Length(Result) = 0) then // no valid sequence found so far
begin
if IsValidSequence([inputArr[i-1],inputArr[i]])
then AddSequence(currentSequence,[inputArr[i-1],inputArr[i]]);
end
else
begin
if IsValidSequence([inputArr[i-2],inputArr[i-1],inputArr[i]]) then
begin
if (Length(currentSequence) = 0) then
AddSequence(currentSequence,[inputArr[i-2],inputArr[i-1],inputArr[i]])
else
AddSequence(currentSequence,inputArr[i]);
end
else // Reset currentSequence
SetLength(currentSequence,0);
end;
// Longer sequence ?
if (Length(currentSequence) > Length(Result)) then
begin
SetLength(Result,0);
AddSequence(Result,currentSequence);
end;
end;
end;
procedure OutputSequence( const arr : TIntArr);
var
i : Integer;
begin
for i := 0 to High(arr) do begin
if i <> High(arr)
then Write(arr[i],',')
else WriteLn(arr[i]);
end;
end;
begin
WriteLn('Longest Arithmetic Sequence:');
OutputSequence(GetLongestSequence(IsValidArithmeticSequence,[0,1,2,3,4,5,6]));
OutputSequence(GetLongestSequence(IsValidArithmeticSequence,[1,0,1,2,3,4,5,6]));
OutputSequence(GetLongestSequence(IsValidArithmeticSequence,[0,0,0,0,0,0]));
OutputSequence(GetLongestSequence(IsValidArithmeticSequence,[0,0,1,2,4,8,16]));
OutputSequence(GetLongestSequence(IsValidArithmeticSequence,[0,0,6,9,12,4,8,16]));
OutputSequence(GetLongestSequence(IsValidArithmeticSequence,[9,12,16]));
OutputSequence(GetLongestSequence(IsValidArithmeticSequence,[1,0,1,-1,-3]));
OutputSequence(GetLongestSequence(IsValidArithmeticSequence,[5,4,78,2,3,4,5,6,18,54,16]));
WriteLn('Longest Geometric Sequence:');
OutputSequence(GetLongestSequence(IsValidGeometricSequence,[0,1,2,3,4,5,6]));
OutputSequence(GetLongestSequence(IsValidGeometricSequence,[1,0,1,2,3,4,5,6]));
OutputSequence(GetLongestSequence(IsValidGeometricSequence,[0,0,0,0,0,0]));
OutputSequence(GetLongestSequence(IsValidGeometricSequence,[0,0,1,2,4,8,16]));
OutputSequence(GetLongestSequence(IsValidGeometricSequence,[0,0,6,9,12,4,8,16]));
OutputSequence(GetLongestSequence(IsValidGeometricSequence,[9,12,16]));
OutputSequence(GetLongestSequence(IsValidGeometricSequence,[1,0,9,-12,16]));
OutputSequence(GetLongestSequence(IsValidGeometricSequence,[5,4,78,2,3,4,5,6,18,54,16]));
ReadLn;
end.
As commented by David, mixing floating point calculations with integers can cause unwanted behavior. Eg. input sequence 9,12,16 with a geometric factor of 4/3 will work here, but other similar non-integer geometric factors may fail. More extensive testing is required to verify this.
In order to remove the dependency of floating point operations, following change in the loop can be made:
// A geometric function is defined by a + n*a + n^2*a + ...
// By comparing sequence[i]*sequence[i-2] with Sqr(sequence[i-1])
// i.e. n^2*a*a with Sqr(n*a) we can establish a valid geometric sequence
q := Sqr(sequence[i-1]);
if (i < 2)
then q1 := q // Special case, always true
else q1 := sequence[i] * sequence[i - 2];
Change the declarations of d,d1,q,q1 to Integer and remove the assignment of q1 before the loop.
The test code is updated to reflect these changes.
There is a problem when a sequence has one or more zeroes for the geometric sequence calculations.
Zero is only considered a member of a geometric sequence if all values are zero.
Geometric sequence: a*r^0, a*r^1, a*r^2, etc; r <> 0.
With a = 0 the progression consists of zeroes only.
This also implies that a valid geometric sequence can not hold both non-zero and zero values.
To rectify this with current structure it became messy. So I updated my test above with a better structured program that handles all input sequences.
This is quite an interesting problem. LU RD has given you an answer that fixes your code. I offer as an alternative, the way I would address the problem:
program LongestSubsequence;
{$APPTYPE CONSOLE}
type
TSubsequence = record
Start: Integer;
Length: Integer;
end;
function Subsequence(Start, Length: Integer): TSubsequence;
begin
Result.Start := Start;
Result.Length := Length;
end;
type
TTestSubsequenceRule = function(a, b, c: Integer): Boolean;
function FindLongestSubsequence(
const seq: array of Integer;
const TestSubsequenceRule: TTestSubsequenceRule
): TSubsequence;
var
StartIndex, Index: Integer;
CurrentSubsequence, LongestSubsequence: TSubsequence;
begin
LongestSubsequence := Subsequence(-1, 0);
for StartIndex := low(seq) to high(seq) do
begin
CurrentSubsequence := Subsequence(StartIndex, 0);
for Index := CurrentSubsequence.Start to high(seq) do
begin
if (CurrentSubsequence.Length<2)
or TestSubsequenceRule(seq[Index-2], seq[Index-1], seq[Index]) then
begin
inc(CurrentSubsequence.Length);
if CurrentSubsequence.Length>LongestSubsequence.Length then
LongestSubsequence := CurrentSubsequence;
end
else
break;
end;
end;
Result := LongestSubsequence;
end;
function TestArithmeticSubsequence(a, b, c: Integer): Boolean;
begin
Result := (b-a)=(c-b);
end;
function FindLongestArithmeticSubsequence(const seq: array of Integer): TSubsequence;
begin
Result := FindLongestSubsequence(seq, TestArithmeticSubsequence);
end;
function TestGeometricSubsequence(a, b, c: Integer): Boolean;
begin
Result := (b*b)=(a*c);
end;
function FindLongestGeometricSubsequence(const seq: array of Integer): TSubsequence;
begin
Result := FindLongestSubsequence(seq, TestGeometricSubsequence);
end;
procedure OutputSubsequence(const seq: array of Integer; const Subsequence: TSubsequence);
var
Index: Integer;
begin
for Index := 0 to Subsequence.Length-1 do
begin
Write(seq[Subsequence.Start + Index]);
if Index<Subsequence.Length-1 then
Write(',');
end;
Writeln;
end;
procedure OutputLongestArithmeticSubsequence(const seq: array of Integer);
begin
OutputSubsequence(seq, FindLongestArithmeticSubsequence(seq));
end;
procedure OutputLongestGeometricSubsequence(const seq: array of Integer);
begin
OutputSubsequence(seq, FindLongestGeometricSubsequence(seq));
end;
begin
Writeln('Testing arithmetic sequences:');
OutputLongestArithmeticSubsequence([]);
OutputLongestArithmeticSubsequence([1]);
OutputLongestArithmeticSubsequence([1,2]);
OutputLongestArithmeticSubsequence([1,2,3]);
OutputLongestArithmeticSubsequence([1,2,4]);
OutputLongestArithmeticSubsequence([6,1,2,4,7]);
OutputLongestArithmeticSubsequence([6,1,2,4,6,7]);
Writeln('Testing geometric sequences:');
OutputLongestGeometricSubsequence([]);
OutputLongestGeometricSubsequence([1]);
OutputLongestGeometricSubsequence([1,2]);
OutputLongestGeometricSubsequence([1,2,4]);
OutputLongestGeometricSubsequence([7,1,2,4,-12]);
OutputLongestGeometricSubsequence([-16,-12,-9]);
OutputLongestGeometricSubsequence([4,-16,-12,-9]);
Readln;
end.
The key point to stress is that your code is hard to understand because all the different aspects are mixed in with each other. I have attempted here to break the algorithm down into smaller parts which can be understood in isolation.
Related
How to determine which number occurs most often in an array?
How do I determine which value occurs the most after I filled the array with 100 random values which are between 1 and 11?
Here is a sample code: procedure TForm1.Button1Click(Sender: TObject); function Calculate: Integer; var Numbers: array [1..100] of Byte; Counts: array [1..11] of Byte; I: Byte; begin // Fill the array with random numbers for I := Low(Numbers) to High(Numbers) do Numbers[I] := Random(11) + 1; // Count the occurencies ZeroMemory(#Counts, SizeOf(Counts)); for I := Low(Numbers) to High(Numbers) do Inc(Counts[Numbers[I]]); // Identify the maximum Result := Low(Counts); for I := Low(Counts) + 1 to High(Counts) do if Counts[I] > Counts[Result] then Result := I; end; begin ShowMessage(Calculate.ToString); end;
It is a simple question [...] Yes but I can't seem to find any straight answers online. You shouldn't be searching for solutions on-line; instead, you should start to think about how to design an algorithm able to solve the problem. For this, you may need pen and paper. First, we need some data to work with: const ListLength = 100; MinValue = 1; MaxValue = 11; function MakeRandomList: TArray<Integer>; begin SetLength(Result, ListLength); for var i := 0 to High(Result) do Result[i] := MinValue + Random(MaxValue - MinValue + 1); end; The MakeRandomList function creates a dynamic array of integers. The array contains ListLength = 100 integers ranging from MinValue = 1 to MaxValue = 11, as desired. Now, given such a list of integers, var L := MakeRandomList; how do we find the most frequent value? Well, if we were to solve this problem without a computer, using only pen and paper, we would probably count the number of times each distinct value (1, 2, ..., 11) occurs in the list, no? Then we would only need to find the value with the greatest frequency. For instance, given the data 2, 5, 1, 10, 1, 5, 2, 7, 8, 5 we would count to find the frequencies X Freq 2 2 5 3 1 2 10 1 7 1 8 1 Then we read the table from the top line to the bottom line to find the row with the greatest frequency, constantly keeping track of the current winner. Now that we know how to solve the problem, it is trivial to write a piece of code that performs this algorithm: procedure FindMostFrequentValue(const AList: TArray<Integer>); type TValueAndFreq = record Value: Integer; Freq: Integer; end; var Frequencies: TArray<TValueAndFreq>; begin if Length(AList) = 0 then raise Exception.Create('List is empty.'); SetLength(Frequencies, MaxValue - MinValue + 1); // Step 0: Label the frequency list items for var i := 0 to High(Frequencies) do Frequencies[i].Value := i + MinValue; // Step 1: Obtain the frequencies for var i := 0 to High(AList) do begin if not InRange(AList[i], MinValue, MaxValue) then raise Exception.CreateFmt('Value out of range: %d', [AList[i]]); Inc(Frequencies[AList[i] - MinValue].Freq); end; // Step 2: Find the winner var Winner: TValueAndFreq; Winner.Value := 0; Winner.Freq := 0; for var i := 0 to High(Frequencies) do if Frequencies[i].Freq > Winner.Freq then Winner := Frequencies[i]; ShowMessageFmt('The most frequent value is %d with a count of %d.', [Winner.Value, Winner.Freq]); end;
Delphi has a TDictionary class, which you can use to implement a frequency map, eg: uses ..., System.Generics.Collections; function MostFrequent(Arr: array of Integer) : Integer; var Frequencies: TDictionary<Integer, Integer>; I, Freq, MaxFreq: Integer; Elem: TPair<Integer, Integer>; begin Frequencies := TDictionary<Integer, Integer>.Create; // Fill the dictionary with numbers for I := Low(Arr) to High(Arr) do begin if not Frequencies.TryGetValue(Arr[I], Freq) then Freq := 0; Frequencies.AddOrSetValue(Arr[I], Freq + 1); end; // Identify the maximum Result := 0; MaxFreq := 0; for Elem in Frequencies do begin if Elem.Value > MaxFreq then begin MaxFreq := Elem.Value; Result := Elem.Key; end; end; Frequencies.Free; end; var Numbers: array [1..100] of Integer; I: Integer; begin // Fill the array with random numbers for I := Low(Numbers) to High(Numbers) do Numbers[I] := Random(11) + 1; // Identify the maximum ShowMessage(IntToStr(MostFrequent(Numbers))); end;
I am also still learning and therefore feel that the way I approached this problem might be a little closer to the way would have done: procedure TForm1.GetMostOccuring; var arrNumbers : array[1..100] of Integer; iNumberWithMost : Integer; iNewAmount, iMostAmount : Integer; I, J : Integer; begin for I := 1 to 100 do arrNumbers[I] := Random(10) + 1; iMostAmount := 0; for I := 1 to 10 do begin iNewAmount := 0; for J := 1 to 100 do if I = arrNumbers[J] then inc(iNewAmount); if iNewAmount > iMostAmount then begin iMostAmount := iNewAmount; iNumberWithMost := I; end; end; ShowMessage(IntToStr(iNumberWithMost)); end; I hope this is not completely useless. It is just a simple answer to a simple question.
B-Spline Curves coefficients - division by zero (code in DELPHI)
I was trying to implement the following recursive formula to my code but to my surprise it turns out that after implementing this to DELPHI, I get an error due to division by zero. I am 98% sure that my knot vector is correctly calculated, which in a way means there shouldn't be any divisions by zero. I am 70% sure that the recursive formula is correctly implemented, for that reason I am posting my code here: program project1; uses SysUtils; Type TRealPoint = record x: single; y: single; end; type TSample = Class(TObject) public KnotVector: array of single; FitPoints: array of TRealPoint; Degree: integer; constructor Create; overload; function Coefficient(i, p: integer; Knot: single): single; procedure GetKnots; destructor Destroy; overload; end; constructor TSample.Create; begin inherited; end; function TSample.Coefficient(i, p: integer; Knot: single): single; var s1, s2: single; begin If (p = 0) then begin If (KnotVector[i] <= Knot) And (Knot < KnotVector[i+1]) then Result := 1.0 else Result := 0.0; end else begin s1 := (Knot - KnotVector[i])*Coefficient(i, p-1, Knot)/(KnotVector[i+p] - KnotVector[i]); //THIS LINE ERRORS due to division by zero ??? s2 := (KnotVector[i+p+1]-Knot)*Coefficient(i+1,p-1,Knot)/(KnotVector[i+p+1]-KnotVector[i+1]); Result := s1 + s2; end; end; procedure TSample.GetKnots(); var KnotValue: single; i, MaxKnot: integer; begin // KNOTS KnotValue:= 0.0; SetLength(KnotVector, Length(FitPoints) + 1 + Degree); MaxKnot:= Length(KnotVector) - (2*Degree + 1); for i := Low(KnotVector) to High(KnotVector) do begin if i <= (Degree) then KnotVector[i] := KnotValue / MaxKnot else if i > Length(FitPoints) then KnotVector[i] := KnotValue / MaxKnot else begin KnotValue := KnotValue + 1.0; KnotVector[i] := KnotValue / MaxKnot; end; end; end; destructor TSample.Destroy; begin inherited; end; var i, j: integer; Test: TSample; N: array of array of single; begin Test := TSample.Create; //define degree Test.Degree := 3; //random fit points j := 15; SetLength(Test.FitPoints, j + 1 + Test.Degree); For i := Low(Test.FitPoints) to High(Test.FitPoints) do begin Test.FitPoints[i].x := Random()*2000; Test.FitPoints[i].y := Random()*2000; end; //get knot vector Test.GetKnots; //get coefficients SetLength(N, j+1, j+1); For j := Low(N) to High(N) do begin For i := Low(N[j]) to High(N[j]) do begin N[j, i] := Test.Coefficient(i,3,Test.KnotVector[j]); write(floattostrf(N[j,i], ffFixed, 2, 2) + ', '); end; writeln(); end; readln(); Test.Free; end. Basically I'm not sure how to continue. I would need the values of matrix N (see this link) of basis coefficients but somehow using the formula from this link leads me to division by zero. So... Is there a totally different way how to calculate those coefficients or what is the problem here? UPDATE Instead of using my own idea i tried to implement the algorithm from here as suggested by Dsm in the comments. As a result, there is no more divison by zero, but the result is totally unexpected anyways. For n + 1 = 10 random fit points with spline degree 3 the basis matrix N (see link) is singular - as seen from the attached image. Instead of that I would expect the matrix to be band matrix. Anyway, here is my updated code: program project1; uses SysUtils; Type TRealPoint = record x: single; y: single; end; type TMatrix = array of array of double; type TSample = Class(TObject) public KnotVector: array of double; FitPoints: array of TRealPoint; SplineDegree: integer; Temp: array of double; A: TMatrix; procedure GetKnots; function GetBasis(Parameter: double): boolean; procedure FormBasisMatrix; end; procedure TSample.GetKnots(); var i, j: integer; begin // KNOTS //https://pages.mtu.edu/~shene/COURSES/cs3621/NOTES/INT-APP/PARA-knot-generation.html SetLength(KnotVector, Length(FitPoints) + SplineDegree + 1); for i := Low(KnotVector) to High(KnotVector) do begin if i <= SplineDegree then KnotVector[i] := 0 else if i <= (High(KnotVector) - SplineDegree - 1) then KnotVector[i] := (i - SplineDegree) / (Length(FitPoints) - SplineDegree) else KnotVector[i] := 1; end; end; function TSample.GetBasis(Parameter: double): boolean; var m, d, k: integer; FirstTerm, SecondTerm: double; begin //http://pages.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/B-spline/bspline-curve-coef.html Result := False; //initialize to 0 SetLength(Temp, Length(FitPoints)); For m := Low(Temp) to High(Temp) do Temp[m] := 0.0; //special cases If Abs(Parameter - KnotVector[0]) < 1e-8 then begin Temp[0] := 1; end else if Abs(Parameter - KnotVector[High(KnotVector)]) < 1e-8 then begin Temp[High(Temp)] := 1; end else begin //find knot span [u_k, u_{k+1}) for k := Low(KnotVector) to High(KnotVector) do if Abs(KnotVector[k] - Parameter) < 1e-8 then break; Temp[k] := 1.0; for d := 1 to SplineDegree do begin Temp[k - d] := (KnotVector[k + 1] - Parameter) * Temp[k - d + 1] / (KnotVector[k + 1] - KnotVector[k - d + 1]); for m := k - d + 1 to k - 1 do begin FirstTerm := (Parameter - KnotVector[m]) / (KnotVector[m + d] - KnotVector[m]); SecondTerm := (KnotVector[m + d + 1] - Parameter) / (KnotVector[m + d + 1] - KnotVector[m + 1]); Temp[m] := FirstTerm * Temp[m] + SecondTerm * Temp[m + 1]; end; Temp[k] := (Parameter - KnotVector[k]) * Temp[k] / (KnotVector[k + d] - KnotVector[k]); end; end; Result := True; end; procedure TSample.FormBasisMatrix; var i, j: integer; begin SetLength(A, Length(FitPoints), Length(FitPoints)); for j := Low(A) to High(A) do begin for i := low(A[j]) to High(A[j]) do //j - row, i - column begin If GetBasis(KnotVector[j + SplineDegree]) then A[j, i] := Temp[i]; end; end; end; var i, j, iFitPoints: integer; Test: TSample; N: array of array of single; begin Test := TSample.Create; //define degree Test.SplineDegree := 3; //random fit points iFitPoints := 10; SetLength(Test.FitPoints, iFitPoints); For i := Low(Test.FitPoints) to High(Test.FitPoints) do begin Test.FitPoints[i].x := Random()*200; Test.FitPoints[i].y := Random()*200; end; //get knot vector Test.GetKnots; //get B-Spline basis matrix Test.FormBasisMatrix; // print matrix for j := Low(Test.A) to High(Test.A) do begin for i := Low(Test.A) to High(Test.A) do write(FloatToStrF(Test.A[j, i], ffFixed, 2, 2) + ', '); writeln(); end; readln(); Test.Free; end.
This does not appear to be the complete answer, but it may help you on your way, and the result is closer to what you expect, but as I say, not completely there. First of all the knots do not look right to me. The knots appear to form a 'ramp' function (clamped line), and though I can't work out if 'm' has any specific value, I would expect the function to be continuous, which yours is not. Making it continuous gives better results, e.g. procedure TSample.GetKnots(); var i, j: integer; iL : integer; begin // KNOTS //https://pages.mtu.edu/~shene/COURSES/cs3621/NOTES/INT-APP/PARA-knot-generation.html iL := Length( FitPoints ); SetLength(KnotVector, iL + SplineDegree + 1); // set outer knot values and sum used to geterate first internal value for i := 0 to SplineDegree - 1 do begin KnotVector[ i ] := 0; KnotVector[ High(KnotVector)-i] := 1; end; // and internal ones for i := 0 to High(KnotVector) - 2* SplineDegree + 1 do begin KnotVector[ SplineDegree + i - 1] := i / (iL - 1); end; end; I introduced iL = Length( Fitpoints ) for convenience - it is not important. The second issue I spotted is more of a programming one. In the GetBasis routine, you evaluate k by breaking a for loop. The problem with that is that k is not guaranteed to persist outside the loop, so your use of it later is not guaranteed to succeed (although it may) Finally, in the same place, your range determination is completely wrong in my opinion. You should be looking for parameter to lie in a half open line segment, but instead you are looking for it to lie close to an endpoint of that line. Putting these two together for k := Low(KnotVector) to High(KnotVector) do if Abs(KnotVector[k] - Parameter) < 1e-8 then break; should be replaced by k1 := 0; for k1 := High(KnotVector) downto Low(KnotVector) do begin if Parameter >= KnotVector[k1] then begin k := k1; break; end; end; where k1 is an integer. I can't help feeling that there is a plus 1 error somewhere, but I can't spot it. Anyway, I hope that this helps you get a bit further.
To build recursive pyramid for coefficient calculation at intervals, you have to start top level of recursion (inner loop of calculations) from the first real (not duplicate) knot index: For i := Test.Degree... Also check the last loop index. P.S. You can remove constructor and destructor from class description and implementation if they have nothing but inherited.
Is there anyway to use the property like behavior?
I have the following formula X := X + F*(1-i div n); Where X, F, i, n: integer; The code I'm using is this F := 7; // the initial speed with no friction. n := 10; // the animation number of steps. Hn := n * 2 ; X := 0; // first Pos i := 1; J := 1; while J < Hn do begin X := X + F * (1 - i div n); if X > Xmax then X := 0; <-- line (1). if i >= n then Dec(i) else Inc(i); Inc(J); end; If it was possible I would like to use this but without class/record implementation(not inside a class/record implementation/method).not the exact syntax, just the same principle, instead of direct assignment to X the SetX is called then the result is assigned to X. X: integer write SetX; // This is not a correct delphi syntax. I added it to explain the behavior I want. function SetX(aValue: integer): integer; const Xmax: SomeIntegerValue; begin if aValue > Xmax then result := 0 else result := aValue; end; So I could omit Line (1). If this was possible, all the lines after the formula would be omitted and the while loop would look like this while J < Hn do // J will be incremented each time the loop wants to read it. begin X := X + F * (1 - i div n); end; Is there anyway to use the property like behavior? Note: I'm looking for a way to alter the assignment and reading ways of a variable like you do in a property of a record/class.
Is there anyway to use the property like approach outside a class/record? No, property getters and setters can only be implemented in records and classes.
You can use local function like procedure YourProcedure; var X: Integer; LJ: Integer; function J: Integer; begin Inc(LJ); Result := LJ; end; procedure SetX(const AValue: Integer); const Xmax: SomeIntegerValue; begin if aValue > Xmax then X := 0 else X := aValue; end; //... begin while J < Hn do // J will be incremented each time the loop wants to read it. begin SetX(X + F * (1 - i div n)); end end.
I found a way to do what I wanted. I know that overloading the := operator is not possible, However forcing the compiler to produce the same behavior as the overloaded operator would behave is possible. The overloading would not let me control the LSA (Left Side Argument). but it gave full control to implicitly convert any TType (in my case it is an integer) to TXinteger. So all I had to do is make sure that every operator would result in a TType which will force the compiler to implicitly convert that to a TXinteger. Forcing the compiler to use my implicit operator every time it wants to assign something to TXinteger means I control the assignment Hence I overloaded the := operator. the following is a test example that makes omitting Line(1) possible. program Project4; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils; type TXinteger = record X: integer; class operator Add(a, b: TXinteger): integer; class operator Add(a: TXinteger; b:integer): integer; class operator Add(a: integer; b:TXinteger): integer; class operator Implicit(a: Integer): TXinteger; class operator Implicit(a: TXinteger): Integer; end; // Example implementation of Add class operator TXinteger.Add(a, b: TXinteger): integer; begin result := a.X + b.X; end;(**) class operator TXinteger.Add(a: TXinteger; b:integer): integer; begin result := a.X + b; end; class operator TXinteger.Add(a: integer; b:TXinteger): integer; begin result := a + b.X; end; class operator TXinteger.Implicit(a: Integer): TXinteger; const Xmax: integer = 10; begin if a > Xmax then result.X := 0 else result.X := a; end; class operator TXinteger.Implicit(a: TXinteger): Integer; begin result := a.X; end; var X: TXinteger; Hn, F, i,J, n: integer; begin try F := 7; n := 10; Hn := n * 2 ; X := 0; i := 1; J := 1; while J < Hn do begin X := X + F * (1 - i div n); // Line (1) is gone now. if i >= n then Dec(i) else Inc(i); Inc(J); end; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end. Note: for this case it is pointless to do all of this just to omit one line of code. I wanted to share this because it gives an idea of how one could overload the := operator. What I wanted is this: Alter how X:Integer is read (value read from the variable x's storage). Alter how X:Integer is assigned. by overloading all the operators that use the value of X, I completed the first. And by forcing the compiler as explained above, I completed the second. Thank you all for your help.
Byte array to Signed integer in Delphi
source array(4 bytes) [$80,$80,$80,$80] =integer 0 [$80,$80,$80,$81] = 1 [$80,$80,$80,$FF] = 127 [$80,$80,$81,$01] = 128 need to convert this to integer. below is my code and its working at the moment. function convert(b: array of Byte): Integer; var i, st, p: Integer; Negative: Boolean; begin result := 0; st := -1; for i := 0 to High(b) do begin if b[i] = $80 then Continue // skip leading 80 else begin st := i; Negative := b[i] < $80; b[i] := abs(b[i] - $80); Break; end; end; if st = -1 then exit; for i := st to High(b) do begin p := round(Power(254, High(b) - i)); result := result + b[i] * p; result := result - (p div 2); end; if Negative then result := -1 * result end; i'm looking for a better function? Update: file link https://drive.google.com/file/d/0ByBA4QF-YOggZUdzcXpmOS1aam8/view?usp=sharing in uploaded file ID field offset is from 5 to 9 NEW: Now i got into new problem which is decoding date field Date field hex [$80,$8F,$21,$C1] -> possible date 1995-12-15 * in uploaded file date field offset is from 199 to 203
Just an example of some improvements as outlined by David. The array is passed by reference as a const. The array is fixed in size. The use of floating point calculations are converted directly into a constant array. Const MaxRange = 3; Type TMySpecial = array[0..MaxRange] of Byte; function Convert(const b: TMySpecial): Integer; var i, j: Integer; Negative: Boolean; Const // Pwr[i] = Round(Power(254,MaxRange-i)); Pwr: array[0..MaxRange] of Cardinal = (16387064,64516,254,1); begin for i := 0 to MaxRange do begin if (b[i] <> $80) then begin Negative := b[i] < $80; Result := Abs(b[i] - $80)*Pwr[i] - (Pwr[i] shr 1); for j := i+1 to MaxRange do Result := Result + b[j]*Pwr[j] - (Pwr[j] shr 1); if Negative then Result := -Result; Exit; end; end; Result := 0; end; Note that less code lines is not always a sign of good performance. Always measure performance before optimizing the code in order to find real bottlenecks. Often code readability is better than optimizing over the top. And for future references, please tell us what the algorithm is supposed to do. Code for testing: const X : array[0..3] of TMySpecial = (($80,$80,$80,$80), // =integer 0 ($80,$80,$80,$81), // = 1 ($80,$80,$80,$FF), // = 127 ($80,$80,$81,$01)); // = 128 var i,j: Integer; sw: TStopWatch; begin sw := TStopWatch.StartNew; for i := 1 to 100000000 do for j := 0 to 3 do Convert(X[j]); WriteLn(sw.ElapsedMilliseconds); ReadLn; end.
Delphi - Sorting real numbers in high, low, high, low order
Say I have the data 1,2,3,4,5,6 I want to sort this data so that it outputs 6 1 5 2 4 3 This way, numbers are matched so that low numbers pair with high numbers Would i use a merge sort to sort it in numerical order, then split the list and match them according to this conditions? I'm trying to sort real number data in a string grid which is read from a data file; I have a working program that sorts these data in numerical order but I'm not sure how to code it so that it sorts in terms of high,low,high,low This is the code for my grid sorting procedure TForm1.SortGrid(Grid: TStringGrid; const SortCol: Integer; //sorting the string grid const datatype: Integer; const ascending: boolean); var i: Integer; tempgrid: TStringGrid; list: array of Integer; begin tempgrid := TStringGrid.create(self); with tempgrid do begin rowcount := Grid.rowcount; ColCount := Grid.ColCount; fixedrows := Grid.fixedrows; end; with Grid do begin setlength(list, rowcount - fixedrows); for i := fixedrows to rowcount - 1 do begin list[i - fixedrows] := i; tempgrid.rows[i].assign(Grid.rows[i]); end; Mergesort(Grid, list, SortCol + 1, datatype, ascending); for i := 0 to rowcount - fixedrows - 1 do begin rows[i + fixedrows].assign(tempgrid.rows[list[i]]) end; row := fixedrows; end; tempgrid.free; setlength(list, 0); end;
First, sort the numbers in descending order by using any algorithm you want (I used bubble sort in example) Then, if you have n elements in array: set a counter going from 1 to (n div 2) take last element and store it in temporary variable (tmp) shift all elements by one place to the right, starting from (counter - 1) * 2 + 1. This would overwrite last element, but you have it stored in tmp var set array[(counter - 1) * 2 + 1] element to tmp end counter This way you would effectively take last element from array and insert it at 1, 3, 5... position, until you insert last half of array elements. Sample code: procedure Sort(var AArray: array of Double); var C1, C2: Integer; tmp : Double; pivot : Integer; begin for C1 := Low(AArray) to High(AArray) - 1 do for C2 := C1 + 1 to High(AArray) do if AArray[C1] < AArray[C2] then begin tmp := AArray[C1]; AArray[C1] := AArray[C2]; AArray[C2] := tmp; end; pivot := Length(AArray) div 2; for C1 := 1 to pivot do begin tmp := AArray[High(AArray)]; for C2 := High(AArray) downto (C1 - 1) * 2 + 1 do AArray[C2] := AArray[C2 - 1]; AArray[(C1 - 1) * 2 + 1] := tmp; end; end;
From sample data you provided above, I am assuming that the input array is presorted. [Note that I don't have a compiler at hand, so you'll have to run it and see that it works --minor fiddling might be needed.] procedure SerratedSort(var AArray: array of Double); var Length1: Integer; Index1: Integer; Temp1: Double; begin Length1 := Length(AArray); Index1 := 0; while Index1 < Length1 do begin Temp1 := AArray[Length1 - 1]; System.Move(AArray[Index1], AArray[Index1 + 1], (Length1 - Index1 + 1) * SizeOf(Double)); AArray[Index1] := Temp1; Index1 := Index1 + 2; end; end; Here is how it (should) work(s) step-by-step Input AArray: 123456 Index1: 0 Temp1 := 6 System.Move: 112345 AArray: 612345 Index1: 2 Temp1 := 5 System.Move: 612234 AArray: 615234 Index1: 4 Temp1 := 4 System.Move: 615233 AArray: 615243 Output AArray: 615243 For a record structure, such as, TPerson, it would be like this: procedure SerratedSort(var A: array of TPerson); var s: Integer; i: Integer; t: TPerson; begin s := Length(A); i := 0; while i < s do begin t := A[s - 1]; System.Move(A[i], A[i + 1], (s - i + 1) * SizeOf(TPerson)); A[i] := t; i := i + 2; end; end;
Sort the data in ascending order. Then pick out the values using the following indices: 0, n-1, 1, n-2, .... In pseudo code the algorithm looks like this: Sort; lo := 0; hi := n-1; while lo<=hi do begin yield lo; inc(lo); if lo>hi then break; yield hi; dec(hi); end;
Example program demonstrating the already above given solutions: program Project1; {$APPTYPE CONSOLE} const Count = 12; type TValues = array[0..Count - 1] of Double; const Input: TValues = (1,2,4,9,13,14,15,23,60,100,101,102); var I: Integer; Output: TValues; procedure ShowValues(Caption: String; Values: TValues); var I: Integer; begin Write(Caption); for I := 0 to Count - 2 do Write(Round(Values[I]), ', '); WriteLn(Round(Values[Count - 1])); end; begin if Odd(Count) then WriteLn('Cannot compute an odd number of input values') else begin WriteLn('Program assumes sorted input!'); ShowValues('Input: ', Input); for I := 0 to (Count div 2) - 1 do begin Output[2 * I] := Input[I]; Output[2 * I + 1] := Input[Count - 1 - I]; end; ShowValues('Output: ', Output); end; ReadLn; end.