First time programming here. So I wrote this function in matlab to find roots of cubic polynomials using iterative processes. The function has to get the number of roots right, so at the end, I used if statements to get rid of a root if it were sufficiently close to any other root I've found, because it's probably a repeated root. However, the problem with this, as I just found out, is that if the coefficients of the polynomial are super small, the roots will all be super close to 0. My code will output that there is only one root, 0, when really it should display 3 solutions of 0.
I feel like this is a rather difficult predicament because the iterative processes will never get the exact numbers twice for a double root, so it isn't a matter of just comparing if the numbers are exactly the same. It could be that they're actually two different roots, just very close to one another. Any suggestions?
Edit: This is the code I wrote to get it to not display double roots twice, but I've realized this could potentially get rid of actual roots.
rts = [root1, root2, root3];
if abs(root1 - root2) < 1*10^(-7)
rts = [root1, root3];
end
if abs(root1 - root3) < 1*10^(-7)
rts = [root1, root2];
end
if abs(root2 - root3) < 1*10^(-7)
rts = [root1, root2];
end
if abs(root1 - root2) < 1*10^(-7) && abs(root1 - root3) < 1*10^(-7)
rts = root1;
end
Assuming that your roots in the rts array are monotolly increasing. The problem with your code is that you overwrite your rts array, depending on which conditions are true. Use a new variable for the unequal roots different_rts.
If the difference of the next root to the last stored root is greater than the treshold, add it to the array.
rts = [ 0 1e-8 0.1 ]
nDifferent = 1; % number of different roots
different_rts = rts(1) % initialize with first value
for ri=2:numel(rts)
if( abs(different_rts(nDifferent)-rts(ri))>1e-7 ) % if difference is greater add next root
nDifferent = nDifferent + 1;
different_rts(nDifferent) = rts(ri);
end
end
Related
I have a scatter plot of approximately 30,000 pts, all of which lie above a horizontal line which I've visually defined in my plot. My goal now is to sum the vertical distance of all of these points to this horizontal line.
The data was read in from a .csv file and is already saved to the workspace, but I also need to check whether a value is NaN, and ignore these.
This is where I'm at right now:
vert_deviation = 0;
idx = 1;
while idx <= numel(my_data(:,5)) && isnan(idx) == 0
vert_deviation = vert_deviation + ((my_data(idx,5) - horiz_line_y_val));
idx = idx + 1;
end
I know that a prerequisite of using the && operator is having two logical statements I believe, but I'm not sure how to rewrite this loop in this way at the moment. I also don't understant why vert_deviation returns NaN at the moment, but I assume this might have to do with the first mistake I described...
I would really appreciate some guidance here - thank you in advance!
EDIT: The 'horizontal line' is a slight oversimplification - in reality the lower limit I need to find the distance to consists of 6 different line segments
I should have specified that the lower limit to which I need to calculate the distance for all scatterplot points varies for different x values (the horizontal line snippet was meant to be a simplification but may have been misleading... apologies for that)
I first modified the data I had already read into the workspace by replacing all NaNvalues with 0. Next, I wrote a while loop which defines the number if indexes to loop through, and defined an && condition to filter out any zeroes. I then wrote a nested if loop which checks what range of x values the given index falls into, and subsequently takes the delta between the y values of a linear line lower limit for that section of the plot and the given point. I repeated this for all points.
while idx <= numel(my_data(:,3)) && not(my_data(idx,3) == 0)
...
if my_data(idx,3) < upper_x_lim && my_data(idx,5) > lower_x_lim
vert_deviation = vert_deviation + (my_data(idx,4) - (m6 * (my_data(idx,5)) + b6))
end
...
m6 and b6 in this case are the slope and y intercept calculated for one section of the plot. The if loop is repeated six times for each section of the lower limit.
I'm sure there are more elegant ways to do this, so I'm open to any feedback if there's room for improvement!
Your loop doesn't exclude NaN values becuase isnan(idx) == 0 checks to see if the index is NaN, rather than checking if the data point is NaN. Instead, check for isnan(my_data(idx,5)).
Also, you can simplify your code using for instead of while:
vert_deviation = 0;
for idx=1:size(my_data,1)
if !isnan(my_data(idx,5))
vert_deviation = vert_deviation + ((my_data(idx,5) - horiz_line_y_val));
end
end
As #Adriaan suggested, you can remove the loop altogether, but it seems that the code in the OP is an oversimplification of the problem. Looking at the additional code posted, I guess it is still possible to remove the loops, but I'm not certain it will be a significant speed improvement. Just use a loop.
I have tried to implement the algorithm described in here to find primitive roots for a prime number.
It works for small prime numbers, however as I try big numbers, it doesn't return correct answers anymore.
I then notice that a^(p-1)/pi tends to be a big number, it returns inf in MATLAB, so I thought factorizing (p-1) could help, but I am failing to see how.
I wrote a small piece of code in MATLABand here it is.
clear all
clc
%works with prime =23,31,37,etc.
prime=761; %doesn't work for this value
F=factor(prime-1); % the factors of prime-1
for i = 2: prime-1
a=i;
tag =1;
for j= 1 :prime-1
if (isprime(j))
p_i = j;
if(mod(a^((prime-1)/p_i),prime)== 1)
tag=0;
break
else
tag = tag +1;
end
end
end
if (tag > 1 )
a %it should only print the primitive root
break
end
end
Any input is welcome.
Thanks
What Matlab does in this case is it calculates a^((p-1)/p) before taking the modulus. As a^((p-1)/p) quite quickly becomes too large to handle, Matlab seems to resolve this by turning it into a floating point number, losing some resolution and yielding the wrong result when you take the modulus.
As mentioned by #rayreng, you could use an arbitrary precision toolbox to resolve this.
Alternatively, you could split the exponentiation into parts, taking the modulus at each stage. This should be faster, as it is less memory intensive. You could dump this in a function and just call that.
% Calculates a^b mod c
i = 0;
result = 1;
while i < b
result = mod(result*a, c);
i = i + 1;
end
this is just part of the code that matters and needs to be fixed. I don't know what i'm doing wrong here. all the variables are simple numbers, it's true that one is needed for that other, but there shouldn't be anything wrong with that. the answer for which I'm getting imaginary numbers is supposed to be part of a loop, so it's important I get it right. please ignore the variables that are not needed, as i just wrote a part of the code
the answer i get is:
KrInitialFirstPart = 0.000000000000000e+00 - 1.466747615972368e+05i
clear all;
clc;
% the initial position components
rInitial= 10; %kpc
zInitial= 0; %kpc
% the initial velocity components
vrInitial= 0; %km/s
vzInitial= 150; %tangential velocity component
vtInitial= 150; %not used
% the height
h= rInitial*vzInitial; %angulan momentum constant
tInitial=0;
Dt=1e-3;
e=0.99;
pc=11613.5;
KrInitialFirstPart= -4*pi*pc*sqrt( 1-(e^2) / (e^3) )*rInitial
format long
Here
sqrt( 1-(e^2) / (e^3) )
you have here
e=0.99;
so e < 1 and so e^3 is less than e^2.
Therefore
(e^2)/(e^3) > 1.
The division operation binds tighter than (i.e is evaluated ahead of) the subtraction so you are taking a square root of a negative number. Hence the imaginary component in your result.
Perhaps you require
sqrt( (1-(e^2)) / (e^3) )
which is guaranteed to yield a real number result since
1 - e^2 > 0
for your specified e
I am trying to write a simple MATLAB program that will find the first chain (more than 70) of consecutive nonzero values and return the starting value of that consecutive chain.
I am working with movement data from a joystick and there are a few thousand rows of data with a mix of zeros and nonzero values before the actual trial begins (coming from subjects slightly moving the joystick before the trial actually started).
I need to get rid of these rows before I can start analyzing the movement from the trials.
I am sure this is a relatively simple thing to do so I was hoping someone could offer insight.
Thank you in advance
EDIT: Here's what I tried:
s = zeros(size(x1));
for i=2:length(x1)
if(x1(i-1) ~= 0)
s(i) = 1 + s(i-1);
end
end
display(S);
for a vector x1 which has a max chain of 72 but I dont know how to find the max chain and return its first value, so I know where to trim. I also really don't think this is the best strategy, since the max chain in my data will be tens of thousands of values.
This answer is generic for any chain size. It finds the longest chain in a vector x1 and retrieves the first element of that chain val.
First we'll use bwlabel to label connected components, For example:
s=bwlabel(x1);
Then we can use tabulate to get a frequency table of s, and find the first element of the biggest connected component:
t=tabulate(s);
[C,I]=max(t(:,2));
val=x1(find(s==t(I,1),1, 'first'));
This should work for the case you have one distinct maximal size chain. What happens for the case if you have more than one chain that has maximal lengths? (you can still use my code with slight modifications...)
You don't need to use an auxiliary vector to keep track of the index:
for i = 1:length(x)
if x(i) ~= 0
count = count + 1;
elseif count >= 70
lastIndex = i;
break;
else
count = 0;
end
if count == 70
index = i - 69;
end
end
To remove all of the elements in the chain from x, you can simply do:
x = x([lastIndex + 1:end]);
EDIT (based off comment):
The reason that the way you did it didn't work was because you didn't reset the counter when you ran into a 0, that's what the:
else
count = 0;
is for; it resets the process, if you will.
For some more clarity, in your original code, this would be reflected by:
if x1(i-1) ~= 0
s(i) = 1 + s(i-1);
else
s(i) = 0;
end
my_fun = z^3 - 1;
my_deriv = 3*(z^2);
Those are the functions I used. I can find the real part of my solutions but when I use a function that has an imaginary part to the root such as the function above, I don't know how to find it. My code goes through different values for my initial value and if I find a root I store it in my array and if it finds a root already in the array it ignores it. So I'm basically wondering can I alter my code to find a root with an imaginary part. Thanks
clc;
close all;
clear all;
Roots = [];
Array_slot = 1;
threshold = 0.00000001;
% X_initial = 1;
for (j = -10:10)
X_initial = j;
if (my_deriv(X_initial) ~= 0)
counter = 0;
while (abs(my_fun(X_initial)) > threshold && counter < 100)
X_initial;
% imag(X_initial)
X_one = X_initial - (my_fun(X_initial)/my_deriv(X_initial));
X_initial = X_one;
% imag(X_one)
counter = counter + 1;
end
if counter < 1000
root = (round(X_initial*1000))/1000;
if ~ismember(root,Roots)
Roots(Array_slot) = root;
Array_slot = Array_slot + 1;
end
end
end
end
Roots
Since your initial point is real, you algorithm would never leave the real axis. To find complex roots, you need to start from a complex point, for instance X_initial = 1i.
I suggest that you revise your algorithm to start once from a real point and once from a complex point, to cover the entire complex plane.
Moreover, ~ismember(root, Roots) doesn't do a great job at filtering out duplicate roots in case of floating point numbers. You'll have to come up with another way to do it. For example, compute the distance from the newly obtained root to each of the old roots, and if its close enough to one of them, it is probably a duplicate so you can discard it.
As a side note, it is recommended not use "i" and "j" as variable names to prevent unexpected behavior, especially when dealing with complex numbers.