Triplet whose sum in range (1,2) - arrays

Given n positive real numbers in an array, find whether there exists a triplet
among this set such that, the sum of the triplet is in the range (1,
2). Do it in linear time and constant space.
the array is not ordered.
numbers are positive
numbers are real numbers
Any help would be greatly appreciated. Thanks.

The trick is to figure out a way to categorize the possible solutions and come up with a linear-time constant-space solution for each.
Consider the three ranges X = (0,2/3), Y = [2/3,1], Z = (1,2). At most one value can come from Z (if two values came from Z, then the sum would exceed 1+1=2). Similarly, at least one value must come from X. Suppose there were 3 values a <= b <= c so that 1 <= a+b+c <= 2 . Then, consider what possible classes of solutions are feasible:
A) `a \in X, b \in X, C \in X`
B) `a \in X, b \in X, C \in Y`
C) `a \in X, b \in X, C \in Z`
D) `a \in X, b \in Y, C \in Y`
E) `a \in X, b \in Y, C \in Z`
So how can we test each case?
Case A is incredibly easy to test: the sum is guaranteed to be below 2, so we just need to test the largest sum (largest 3 elements in X) exceeds 1.
Case C is incredibly easy to test: since the sum is guaranteed to be above 1, we only need to check if the sum is below 2. So in order to do that, we just need to test the smallest 2 values in X and the smallest value in Z
Cases D and E are similar to C (since the sum must be at least 4/3 > 1, choose the smallest possible values in each class).
Case B is the only tricky case. 0 < a+b < 4/3 and 2/3 <= c <= 1. To handle case B, we consider these intervals : X1 = (0, 1/2), X2 = [1/2 2/3), Y = [2/3, 1].
This results in following three valid cases :
B1. a in X1, b in X2, c in Y
B2. a in X1, b in X1, c in Y
B3. a in X2, b in X2, c in Y
Case B1 & B3 : Sum of three numbers is always greater than 1 so we take minimum values and check if it is smaller than 2 or not.
Case B2 : Sum of three numbers is always less than 2, so we take maximum sum and check if is greater than 1 or not.
So to summarize, the tests are:
|X| >= 3 and Xmax(1) + Xmax(2) + Xmax(3) >= 1
|X| >= 2, |Z| >= 1, and Xmin(1)+Xmin(2)+Zmin(1) <= 2
|X| >= 1, |Y| >= 2, and Xmin(1)+Ymin(1)+Ymin(2) <= 2
|X| >= 1, |Y| >= 1, |Z| >= 1, and Xmin(1)+Ymin(1)+Zmin(1) <= 2
|X| >= 2, |Y| >= 1, and Xmax(1) + Xmax(2) + Ymin(1) < 2
|X| >= 2, |Y| >= 1, and Xmin(1) + Xmin(2) + Ymax(1) > 1)
Each test can be performed in linear time and constant space (you only need to find Xmax(1), Xmax(2), Xmax(3), Xmin(1), Xmin(2), Ymin(1), Ymin(2), Ymax(1), Zmin(1), all of which can be found in one pass even if the data is not sorted)

So, you have an array of double data types of length n. Intialize three variables a,b and c as first 3 values of array.Now,iterate from i = 3 to n and check the following: 1)Check if sum falls in (1, 2),if it does then return true. 2)If not, then check if sum is greater than 2,if so, then replace MAX(a,b,c) to current element arr[i]. 3)Otherwise sum must be less than 1 then replace MIN(a,b,c) to current element arr[i].And finally after coming out of loop check once again for last triplet if sum falls in (1,2) then return true,otherwise return false.
enter code here
double a=arr[0], b=arr[1], c=arr[2];
for(int i=3 ; i<n ; i++){
// check if sum fall in (1, 2)
if(a+b+c > 1 && a+b+c < 2){
return 1;
}
// if not, then check is sum greater than 2
// if so, then replece MAX(a,b,c) to new number
else if(a+b+c > 2){
if(a>b && a>c){
a = arr[i];
}
else if(b>a && b>c){
b = arr[i];
}
else if(c>a && c>b){
c = arr[i];
}
}
// else then sum must be less than 1
// then replace MIN(a,b,c) to new number
else{
if(a<b && a<c){
a = arr[i];
}
else if(b<a && b<c){
b = arr[i];
}
else if(c<a && c<b){
c = arr[i];
}
}
}
// check for last a, b, c triplet
if(a+b+c > 1 && a+b+c < 2){
return 1;
}
else{
return 0;
}

Question asked is similar to this InterviewBit question. I've made some changes to the solution mentioned below to match exactly what you asked.
There are three conditions:
First, if the sum is between 1 and 2.
Second, if the sum is greater than 2, then the larger element of the three will be replaced with the next element and the process is repeated.
Third, if the sum is less than 1, then the smaller element of the three will be replaced with the next element and same process is repeated.
int Solution::solve(vector<float> &A) {
if(A.size()<3) return 0;
float a = A[0];
float b = A[1];
float c = A[2];
for(int i=3;i<A.size();++i){
if(a+b+c>1 && a+b+c<2)
return 1;
float temp = A[i];
if(a+b+c>=2){
if(a>b && a>c)
a = temp;
else if(b>c && b>a)
b = temp;
else
c = temp;
}
else{
if(a<b && a<c)
a = temp;
else if(b<c && b<a)
b = temp;
else
c = temp;
}
}
if(a+b+c>1 && a+b+c<2) return 1;
return 0;
}
Same question can also be solved using two pointer technique. In this first we'll have to sort the array. But, It's complexity will be more than O(logn).
int Solution::solve(vector<float> &A) {
int n = A.size();
if(n<3) return 0;
sort(A.begin(), A.end());
int l = 0, r = n-1;
while(l<r-1){
float s = A[l]+A[l+1]+A[r];
if(s>=2)
r = r-1;
else if(s<1)
l = l+1;
else return 1;
}
return 0;
}

This problem can easily be solved in linear runtime time using the sliding window sum approach. In this case, we will use a window of size 3. This is also called the 'moving sum algorithm'.
Algorithm Below
1> Prepare the window of size 3 with the first 3 elements
2> IF (array.len <= 3): CHECK IF window-sum is in the range (1,2), then RETURN accordingly
3> FOR i = 3 UPTO (array.len-1)
3.1> SORT the window (3log3 = constant time operation)
3.2> IF window-sum is in the range (1,2): RETURN 1 or TRUE
3.3> ELSE IF window-sum < 1: Replace the smallest element in the window (window[0]) with array[i]
3.4> ELSE IF window-sum > 2: Replace the largest element in the window (window[2]) with array[i]
4> Outside the loop, check the FINAL window sum and RETURN accordingly.
Access the Python code here. Star the repository, please!

Java code for the solution given by #soul Ec.
we need to modify the case B.
let say our numbers are a+b+c
there are three ranges
x1 x2 y
(0,1/2) (1/2,2/3) (2/3,1)
we have 4 possibilities
1. x1 + x1 +y
2. x2 + x2 +y
3. x1 + x2 +y
4 x2 + x1 +y
here case 3 and 4 are identical as sum of it will be same. so we have 3 cases only.
1. x1 + x1 + y it is always <2 ( do x1max+x1max+ymax <2 to verify)
so we have to check if x1max(1)+x1max(2)+ymax(1) > 1
2. x2 + x2 + y it is always >1 ( do x2min+x2min+ymin >1 to verify)
so we have to check if x2min(1)+x2min(2)+ymin(1) <=2
3. x1 + x2 + y it is always >1 (do x1min+x2min+ymin >1 to verify)
so we have to check if x1min(1)+x2min(1)+ymin(1)<=2
public static int solve(ArrayList<String> A) {
double d[]= new double[A.size()];
for(int i=0;i<A.size();i++) {
d[i]= Double.parseDouble(A.get(i));
}
double range1 = 0;
double range2 = (double) 2/3;
double range3 = 1;
double range4 = 2;
double range02 =(double) 1/2;
// min and max in range (0,2/3)
double min1= Double.MAX_VALUE;
double min2=Double.MAX_VALUE;
double min3=Double.MAX_VALUE;
double max1= Double.MIN_VALUE;
double max2=Double.MIN_VALUE;
double max3=Double.MIN_VALUE;
// min and max in range (2/3,1)
double miny1= Double.MAX_VALUE;
double miny2=Double.MAX_VALUE;
double miny3=Double.MAX_VALUE;
double maxy1= Double.MIN_VALUE;
double maxy2=Double.MIN_VALUE;
double maxy3=Double.MIN_VALUE;
// min and max in range (1,2)
double minz1= Double.MAX_VALUE;
double minz2=Double.MAX_VALUE;
double minz3=Double.MAX_VALUE;
double maxz1= Double.MIN_VALUE;
double maxz2=Double.MIN_VALUE;
double maxz3=Double.MIN_VALUE;
// min and max in range (0,1/2)
double minxx1= Double.MAX_VALUE;
double minxx2=Double.MAX_VALUE;
double minxx3=Double.MAX_VALUE;
double maxx1= Double.MIN_VALUE;
double maxx2=Double.MIN_VALUE;
double maxx3=Double.MIN_VALUE;
// min and max in range (1/2,2/3)
double minyy1= Double.MAX_VALUE;
double minyy2=Double.MAX_VALUE;
double minyy3=Double.MAX_VALUE;
double maxyy1= Double.MIN_VALUE;
double maxyy2=Double.MIN_VALUE;
double maxyy3=Double.MIN_VALUE;
for (int i = 0; i < d.length; i++) {
if (d[i] >= range1 && d[i] < range02) {
if (d[i] < minxx3) {
minxx1=minxx2;
minxx2=minxx3;
minxx3 = d[i];
} else if (d[i] > minxx3 && d[i] < minxx2) {
minxx1=minxx2;
minxx2 = d[i];
} else if (d[i] > minxx3 && d[i] > minxx2 && d[i] < minxx1) {
minxx1 = d[i];
}
if (d[i] > maxx3) {
maxx1=maxx2;
maxx2=maxx3;
maxx3 = d[i];
} else if (d[i] < maxx3 && d[i] > maxx2) {
maxx1=maxx2;
maxx2 = d[i];
} else if (d[i] < maxx3 && d[i] < maxx2 && d[i] > maxx1) {
maxx1 = d[i];
}
}
if (d[i] >= range02 && d[i] < range2) {
if (d[i] < minyy3) {
minyy1=minyy2;
minyy2=minyy3;
minyy3 = d[i];
} else if (d[i] > minyy3 && d[i] < minyy2) {
minyy1=minyy2;
minyy2 = d[i];
} else if (d[i] > minyy3 && d[i] > minyy2 && d[i] < minyy1) {
minyy1 = d[i];
}
if (d[i] > maxyy3) {
maxyy1=maxyy2;
maxyy2=maxyy3;
maxyy3 = d[i];
} else if (d[i] < maxyy3 && d[i] > maxyy2) {
maxyy1=maxyy2;
maxyy2 = d[i];
} else if (d[i] < maxyy3 && d[i] < maxyy2 && d[i] > maxyy1) {
maxyy1 = d[i];
}
}
if (d[i] >= range1 && d[i] < range2) {
if (d[i] < min3) {
min1=min2;
min2=min3;
min3 = d[i];
} else if (d[i] > min3 && d[i] < min2) {
min1=min2;
min2 = d[i];
} else if (d[i] > min3 && d[i] > min2 && d[i] < min1) {
min1 = d[i];
}
if (d[i] > max3) {
max1=max2;
max2=max3;
max3 = d[i];
} else if (d[i] < max3 && d[i] > max2) {
max1=max2;
max2 = d[i];
} else if (d[i] < max3 && d[i] < max2 && d[i] > max1) {
max1 = d[i];
}
}
if (d[i] >= range2 && d[i] < range3) {
if (d[i] < miny3) {
miny1=miny2;
miny2=miny3;
miny3 = d[i];
} else if (d[i] > miny3 && d[i] < miny2) {
miny1=miny2;
miny2 = d[i];
} else if (d[i] > miny3 && d[i] > miny2 && d[i] < miny1) {
miny1 = d[i];
}
if (d[i] > maxy3) {
maxy1=maxy2;
maxy2=maxy3;
maxy3 = d[i];
} else if (d[i] < maxy3 && d[i] > maxy2) {
maxy1=maxy2;
maxy2 = d[i];
} else if (d[i] < maxy3 && d[i] < maxy2 && d[i] > maxy1) {
maxy1 = d[i];
}
}
if (d[i] >= range3 && d[i] <= range4) {
if (d[i] < minz3) {
minz1=minz2;
minz2=minz3;
minz3 = d[i];
} else if (d[i] > minz3 && d[i] < minz2) {
minz1=minz2;
minz2 = d[i];
} else if (d[i] > minz3 && d[i] > minz2 && d[i] < minz1) {
minz1 = d[i];
}
if (d[i] > maxz3) {
maxz1=maxz2;
maxz2=maxz3;
maxz3 = d[i];
} else if (d[i] < maxz3 && d[i] > maxz2) {
maxz1=maxz2;
maxz2 = d[i];
} else if (d[i] < maxz3 && d[i] < maxz2 && d[i] > maxz1) {
maxz1 = d[i];
}
}
}
if(max1+max2+max3>=1 && max1!=Double.MIN_VALUE && max2!=Double.MIN_VALUE && max3!=Double.MIN_VALUE)
return 1;
if(min3+min2+minz3<=2 && min3!=Double.MAX_VALUE && min2!=Double.MAX_VALUE && minz3!=Double.MAX_VALUE )
return 1;
if(min3+miny3+miny2<=2 && min3!=Double.MAX_VALUE && miny3!=Double.MAX_VALUE && miny2!=Double.MAX_VALUE)
return 1;
if(min3+miny3+minz3<=2 && min3!=Double.MAX_VALUE && miny3!=Double.MAX_VALUE && minz3!=Double.MAX_VALUE)
return 1;
if(maxx3+maxx2+maxy3>1 && maxx3!=Double.MIN_VALUE && maxx2!=Double.MIN_VALUE && maxy3!=Double.MIN_VALUE) {
return 1;
}
if(minyy3+minyy2+miny3<=2 && minyy3!=Double.MAX_VALUE && minyy2!=Double.MAX_VALUE && miny3!=Double.MAX_VALUE) {
return 1;
}
if(minxx3+minyy3+miny3<=2 && minxx3!=Double.MAX_VALUE && minyy3!=Double.MAX_VALUE && miny3!=Double.MAX_VALUE) {
return 1;
}
return 0;
}

the solution is in c++(interviewbbit solution)
int Solution::solve(vector<string> &arr) {
int n=arr.size(),i;
vector<float>v;
for(i=0;i<n;i++)
{
v.push_back(stof(arr[i]));
}
float a=v[0],b=v[1],c=v[2];
float mx=0;
for(i=3;i<n;i++)
{
if(a+b+c<2 && a+b+c>1)
return 1;
else if(a+b+c>2)
{
if(a>b && a>c)
a=v[i];
else if(b>a && b>c)
b=v[i];
else
c=v[i];
}
else
{
if(a<b && a<c)
a=v[i];
else if(b<a && b<c)
b=v[i];
else
c=v[i];
}
}
if(a+b+c>1 && a+b+c<2)
return 1;
else
return 0;
}

We can easily do it in O(n) , we just need to find three minimum positive real numbers ,which we can find in one iteration and in the end if their summation lies in between (1,2) then return 1 else return 0.

The problem in its whole as stated is undecidable. This is due to the fact that for any two real numbers a and b it cannot be decided if a > b holds (also see this answer of me). But you have to do at least one comparison of a real number against an integer value to solve that problem. Comparing against an integer doesn't make the problem easier since you could have a real number which is 2,00...001 where there is an arbitrary number of zeros between the digits 2 and 1 which you don't know beforehand. Storing such real numbers (probably not every one, but many of them) in the main memory of a computer is not a big problem for such specific ones, since they can be represented by an approximation algorithm.
for further information I suggest reading Complexity Theory of Real Functions

Related

Search unsorted array for 3 elements which sum to a value

I am trying to make an algorithm, of Θ( n² ).
It accepts an unsorted array of n elements, and an integer z,
and has to return 3 indices of 3 different elements a,b,c ; so a+b+c = z.
(return NILL if no such integers were found)
I tried to sort the array first, in two ways, and then to search the sorted array.
but since I need a specific running time for the rest of the algorithm, I am getting lost.
Is there any way to do it without sorting? (I guess it does have to be sorted) either with or without sorting would be good.
example:
for this array : 1, 3, 4, 2, 6, 7, 9 and the integer 6
It has to return: 0, 1, 3
because ( 1+3+2 = 6)
Algorithm
Sort - O(nlogn)
for i=0... n-1 - O(1) assigning value to i
new_z = z-array[i] this value is updated each iteration. Now, search for new_z using two pointers, at begin (index 0) and end (index n-1) If sum (array[ptr_begin] + array[ptr_ens]) is greater then new_z, subtract 1 from the pointer at top. If smaller, add 1 to begin pointer. Otherwise return i, current positions of end and begin. - O(n)
jump to step 2 - O(1)
Steps 2, 3 and 4 cost O(n^2). Overall, O(n^2)
C++ code
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
std::vector<int> vec = {3, 1, 4, 2, 9, 7, 6};
std::sort(vec.begin(), vec.end());
int z = 6;
int no_success = 1;
//std::for_each(vec.begin(), vec.end(), [](auto const &it) { std::cout << it << std::endl;});
for (int i = 0; i < vec.size() && no_success; i++)
{
int begin_ptr = 0;
int end_ptr = vec.size()-1;
int new_z = z-vec[i];
while (end_ptr > begin_ptr)
{
if(begin_ptr == i)
begin_ptr++;
if (end_ptr == i)
end_ptr--;
if ((vec[begin_ptr] + vec[end_ptr]) > new_z)
end_ptr--;
else if ((vec[begin_ptr] + vec[end_ptr]) < new_z)
begin_ptr++;
else {
std::cout << "indices are: " << end_ptr << ", " << begin_ptr << ", " << i << std::endl;
no_success = 0;
break;
}
}
}
return 0;
}
Beware, result is the sorted indices. You can maintain the original array, and then search for the values corresponding to the sorted array. (3 times O(n))
The solution for the 3 elements which sum to a value (say v) can be done in O(n^2), where n is the length of the array, as follows:
Sort the given array. [ O(nlogn) ]
Fix the first element , say e1. (iterating from i = 0 to n - 1)
Now we have to find the sum of 2 elements sum to a value (v - e1) in range from i + 1 to n - 1. We can solve this sub-problem in O(n) time complexity using two pointers where left pointer will be pointing at i + 1 and right pointer will be pointing at n - 1 at the beginning. Now we will move our pointers either from left or right depending upon the total current sum is greater than or less than required sum.
So, overall time complexity of the solution will be O(n ^ 2).
Update:
I attached solution in c++ for the reference: (also, added comments to explain time complexity).
vector<int> sumOfthreeElements(vector<int>& ar, int v) {
sort(ar.begin(), ar.end());
int n = ar.size();
for(int i = 0; i < n - 2 ; ++i){ //outer loop runs `n` times
//for every outer loop inner loops runs upto `n` times
//therefore, overall time complexity is O(n^2).
int lo = i + 1;
int hi = n - 1;
int required_sum = v - ar[i];
while(lo < hi) {
int current_sum = ar[lo] + ar[hi];
if(current_sum == required_sum) {
return {i, lo, hi};
} else if(current_sum > required_sum){
hi--;
}else lo++;
}
}
return {};
}
I guess this is similar to LeetCode 15 and 16:
LeetCode 16
Python
class Solution:
def threeSumClosest(self, nums, target):
nums.sort()
closest = nums[0] + nums[1] + nums[2]
for i in range(len(nums) - 2):
j = -~i
k = len(nums) - 1
while j < k:
summation = nums[i] + nums[j] + nums[k]
if summation == target:
return summation
if abs(summation - target) < abs(closest - target):
closest = summation
if summation < target:
j += 1
elif summation > target:
k -= 1
return closest
Java
class Solution {
public int threeSumClosest(int[] nums, int target) {
Arrays.sort(nums);
int closest = nums[0] + nums[nums.length >> 1] + nums[nums.length - 1];
for (int first = 0; first < nums.length - 2; first++) {
int second = -~first;
int third = nums.length - 1;
while (second < third) {
int sum = nums[first] + nums[second] + nums[third];
if (sum > target)
third--;
else
second++;
if (Math.abs(sum - target) < Math.abs(closest - target))
closest = sum;
}
}
return closest;
}
}
LeetCode 15
Python
class Solution:
def threeSum(self, nums):
res = []
nums.sort()
for i in range(len(nums) - 2):
if i > 0 and nums[i] == nums[i - 1]:
continue
lo, hi = -~i, len(nums) - 1
while lo < hi:
tsum = nums[i] + nums[lo] + nums[hi]
if tsum < 0:
lo += 1
if tsum > 0:
hi -= 1
if tsum == 0:
res.append((nums[i], nums[lo], nums[hi]))
while lo < hi and nums[lo] == nums[-~lo]:
lo += 1
while lo < hi and nums[hi] == nums[hi - 1]:
hi -= 1
lo += 1
hi -= 1
return res
Java
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> res = new LinkedList<>();
for (int i = 0; i < nums.length - 2; i++) {
if (i == 0 || (i > 0 && nums[i] != nums[i - 1])) {
int lo = -~i, hi = nums.length - 1, sum = 0 - nums[i];
while (lo < hi) {
if (nums[lo] + nums[hi] == sum) {
res.add(Arrays.asList(nums[i], nums[lo], nums[hi]));
while (lo < hi && nums[lo] == nums[-~lo])
lo++;
while (lo < hi && nums[hi] == nums[hi - 1])
hi--;
lo++;
hi--;
} else if (nums[lo] + nums[hi] < sum) {
lo++;
} else {
hi--;
}
}
}
}
return res;
}
}
Reference
You can see the explanations in the following links:
LeetCode 15 - Discussion Board
LeetCode 16 - Discussion Board
LeetCode 15 - Solution
You can use something like:
def find_3sum_restr(items, z):
# : find possible items to consider -- O(n)
candidates = []
min_item = items[0]
for i, item in enumerate(items):
if item < z:
candidates.append(i)
if item < min_item:
min_item = item
# : find possible couples to consider -- O(n²)
candidates2 = []
for k, i in enumerate(candidates):
for j in candidates[k:]:
if items[i] + items[j] <= z - min_item:
candidates2.append([i, j])
# : find the matching items -- O(n³)
for i, j in candidates2:
for k in candidates:
if items[i] + items[j] + items[k] == z:
return i, j, k
This O(n + n² + n³), hence O(n³).
While this is reasonably fast for randomly distributed inputs (perhaps O(n²)?), unfortunately, in the worst case (e.g. for an array of all ones, with a z > 3), this is no better than the naive approach:
def find_3sum_naive(items, z):
n = len(items)
for i in range(n):
for j in range(i, n):
for k in range(j, n):
if items[i] + items[j] + items[k] == z:
return i, j, k

Pattern for action decision

I am writing maze generator and at the some point I have to choose random unvisited neighbour of a cell. The first idea was just to enumerate neighbours such as left = 0, top = 1, right = 2, bottom = 3 and use rand() % 4 to generate random number and choose appropriate cell. However, not all cells features 4 neighbours, so that I had to write following code:
Cell* getRandomNeighbour(const Maze* const maze, const Cell* const currentCell) {
int randomNumb = rand() % 4;
int timer = 1;
while(timer > 0) {
if (randomNumb == 0 && currentCell->x < maze->width-1 && maze->map[currentCell->y][currentCell->x+1].isUnvisited)
return &maze->map[currentCell->y][currentCell->x+1];
if (randomNumb == 1 && currentCell->x > 0 && maze->map[currentCell->y][currentCell->x-1].isUnvisited)
return &maze->map[currentCell->y][currentCell->x-1];
if (randomNumb == 2 && currentCell->y < maze->height-1 && maze->map[currentCell->y+1][currentCell->x].isUnvisited)
return &maze->map[currentCell->y+1][currentCell->x];
if (randomNumb == 3 && currentCell->y > 0 && maze->map[currentCell->y-1][currentCell->x].isUnvisited)
return &maze->map[currentCell->y-1][currentCell->x];
timer--;
randomNumb = rand() % 4;
}
if (currentCell->x < maze->width-1 && maze->map[currentCell->y][currentCell->x+1].isUnvisited)
return &maze->map[currentCell->y][currentCell->x+1];
if (currentCell->x > 0 && maze->map[currentCell->y][currentCell->x-1].isUnvisited)
return &maze->map[currentCell->y][currentCell->x-1];
if (currentCell->y < maze->height-1 && maze->map[currentCell->y+1][currentCell->x].isUnvisited)
return &maze->map[currentCell->y+1][currentCell->x];
if (currentCell->y > 0 && maze->map[currentCell->y-1][currentCell->x].isUnvisited)
return &maze->map[currentCell->y-1][currentCell->x];
return NULL;
}
So, if after 10 iterations the right decision isn't chosen, it will be picked by brute force. This approach seems to be good for the reason that varying of variable timer changes the complexity of maze: the less timer is, the more straightforward maze is. Nevertheless, if my only purpose is to generate completely random maze, it takes a lot of execution time and look a little bit ugly. Is there any pattern(in C language) or way of refactoring that could enable me to deal with this situation without long switches and a lot of if-else constructions?
As #pat and #Ivan Gritsenko suggested, you can limit your random choice to the valid cells only, like this:
Cell* getRandomNeighbour(const Maze* const maze, const Cell* const currentCell)
{
Cell *neighbours[4] = {NULL};
int count = 0;
// first select the valid neighbours
if ( currentCell->x < maze->width - 1
&& maze->map[currentCell->y][currentCell->x + 1].isUnvisited ) {
neighbours[count++] = &maze->map[currentCell->y][currentCell->x + 1];
}
if ( currentCell->x > 0
&& maze->map[currentCell->y][currentCell->x - 1].isUnvisited ) {
neighbours[count++] = &maze->map[currentCell->y][currentCell->x - 1];
}
if ( currentCell->y < maze->height - 1
&& maze->map[currentCell->y + 1][currentCell->x].isUnvisited ) {
neighbours[count++] = &maze->map[currentCell->y + 1][currentCell->x];
}
if ( currentCell->y > 0
&& maze->map[currentCell->y - 1][currentCell->x].isUnvisited ) {
neighbours[count++] = &maze->map[currentCell->y - 1][currentCell->x];
}
// then choose one of them (if any)
int chosen = 0;
if ( count > 1 )
{
int divisor = RAND_MAX / count;
do {
chosen = rand() / divisor;
} while (chosen >= count);
}
return neighbours[chosen];
}
The rationale behind the random number generation part (as opposed to the more common rand() % count) is well explained in this answer.
Factoring repeated code, and a more disciplined way of picking the order of directions to try yields this:
// in_maze returns whether x, y is a valid maze coodinate.
int in_maze(const Maze* const maze, int x, int y) {
return 0 <= x && x < maze->width && 0 <= y && y < maze->height;
}
Cell *get_random_neighbour(const Maze* const maze, const Cell* const c) {
int dirs[] = {0, 1, 2, 3};
// Randomly shuffle dirs.
for (int i = 0; i < 4; i++) {
int r = i + rand() % (4 - i);
int t = dirs[i];
dirs[i] = dirs[r];
dirs[r] = t;
}
// Iterate through the shuffled dirs, returning the first one that's valid.
for (int trial=0; trial<4; trial++) {
int dx = (dirs[trial] == 0) - (dirs[trial] == 2);
int dy = (dirs[trial] == 1) - (dirs[trial] == 3);
if (in_maze(maze, c->x + dx, c->y + dy)) {
const Cell * const ret = &maze->map[c->y + dy][c->x + dx];
if (ret->isUnvisited) return ret;
}
}
return NULL;
}
(Disclaimer: untested -- it probably has a few minor issues, for example const correctness).

Optimizing a nested loop in C

I have a nested loop to find all possible combinations of numbers between 1 and x in groups of 4, where a < b < c < d.
A method is called as each group is discovered to do a simple equivalency test on the sum of those numbers.
The loop does work and produces expected output (1 set of numbers for this particular x), however it takes 12+ seconds to find this answer and another ~5 to test the remaining possibilities, which is definitely bad, considering the x values are < 1000.
I tried having the outer loop iterate a < x - 3 times, the b loop b < x - 2 times, down to d < x times which didn't make a noticeable difference.
What would be a better approach in changing this loop?
for (a = 1; a < x; a++) {
for (b = a + 1; b < x; b++) {
for (c = b + 1; c < x; c++) {
for (d = c + 1; d < x; d++) {
check(a, b, c, d);
}
}
}
}
With such a deep level of nesting, any early exit you can introduce - particularly at the outer loops - could net big gains.
For example, you write that check is testing a + b + c + d == x && a * b * c * d == x - so you can compute the intermediate sum and product, and break when you encounter numbers that would make any selection of later numbers impossible.
An example:
for (a = 1; a < x; a++) {
for (b = a + 1; b < x; b++) {
int sumAB = a + b;
if (sumAB + sumAB > x) break;
int prodAB = a * b;
if (prodAB * prodAB > x) break;
for (c = b + 1; c < x; c++) {
int sumABC = sumAB + c;
if (sumABC + c > x) break;
int prodABC = prodAB * c;
if (prodABC * c > x) break;
for (d = c + 1; d < x; d++) {
int sumABCD = sumABC + d;
if (sumABCD > x) break;
if (sumABCD != x) continue;
int prodABCD = prodABC * d;
if (prodABCD > x) break;
if (prodABCD != x) continue;
printf("%d, %d, %d, %d\n", a, b, c, d);
}
}
}
}
This is just an example - you can constrain all the checks here further (e.g. make the first check be sumAB + sumAB + 3 > x). The point is to focus on early exits.
I added a counter for each loop, counting how many times it was entered, and tried your version and my version, with x = 100. My version has orders of magnitude less loop entries:
No early exits: 99, 4851, 156849, 3764376
With early exits: 99, 4851, 1122, 848

For loop and While loop to select values in C

Could someone help me to look at the code I wrote in C? It can generate numbers, but the if condition looks like wrong, but I donot know how to fix it.
I need to get some n random values with distribution and these values are in a special intervals, so I use for loop like below:
n=10; mu=2; mu_plus=3; p=0.2;
for(i=1; i<=n; i++)
{
x1 = gasdev(&idum);
x2 = gasdev(&idum);
z = sqrt(1-p*p)*x1 + p*x2 + mu;
z_p= x2 + mu_plus;
if (z > 2.17 || z<-2.17)
{
Z[i]=z;
Z_plus[count]=z_p;
}
printf("%d %lf %lf\n", i, Z[i], Z_plus[i]);
}
Where gasdev() is a function for generating random value with a standard normal distribution, Z and Z_plus are 1*n vectors. The results are mess, so I think that the IF condition must be wrong. Could any one could help me? Thank you.
I also tried the While loop.
while(count < n)
{
x1 = gasdev(&idum);
x2 = gasdev(&idum);
z = sqrt(1-p*p)*x1 + p*x2 + mu;
z_p= x2 + mu_plus;
if (z > 2.17 || z<-2.17)
{
count++;
Z[count]=z;
Z_plus[count]=z_p;
}
printf("%d %lf\n",count, Z[count]);
if (count >n) break;
}
It can print out normally, but occurs an error at the end.
Thank you!
The error here is that you always increment i as part of the for loop so that if z is outside the range of the bracket set, you do not put any value into the Z array. You do not specify count so was that supposed to be i or not?
n=10; mu=2; mu_plus=3; p=0.2;
for(i=1; i<=n; i++)
{
x1 = gasdev(&idum);
x2 = gasdev(&idum);
z = sqrt(1-p*p)*x1 + p*x2 + mu;
z_p= x2 + mu_plus;
if (z > 2.17 || z<-2.17)
{
Z[i]=z;
Z_plus[count]=z_p; // Should this be Z_plus[i] ??
}
// Note that if outside of bracket no value put in Z[i]
// This makes Z[i] and Z_plus[i] garbage
printf("%d %lf %lf\n", i, Z[i], Z_plus[i]);
}
Your while loop is not correct either as it increments count when it is n-1 and tries to process Z[n] when it appears that you must have defined it as an array of size n (max index is n-1). Also the print needs to be inside the if not outside.
while(count < n)
{
x1 = gasdev(&idum);
x2 = gasdev(&idum);
z = sqrt(1-p*p)*x1 + p*x2 + mu;
z_p= x2 + mu_plus;
if (z > 2.17 || z<-2.17)
{
// This allows count == n which overflows the buffer.
count++;
Z[count]=z;
Z_plus[count]=z_p;
}
// This should be inside the bracket not outside
printf("%d %lf\n",count, Z[count]);
// This is not needed since it will exit the while at count == n
if (count >n) break;
}
correct code would be
count = 0;
while(count < n)
{
x1 = gasdev(&idum);
x2 = gasdev(&idum);
z = sqrt(1-p*p)*x1 + p*x2 + mu;
z_p= x2 + mu_plus;
if (z > 2.17 || z<-2.17)
{
Z[count]=z;
Z_plus[count]=z_p;
printf("%d %lf\n",count, Z[count]);
count++;
}
}

Logarithm computing without math.h

I'm trying to compute ln(x) by Taylor series. Here is my code:
#define N 10
float ln(float x){
int i;
float result;
float xt;
float xtpow;
int sign;
if(x > 0 && x <= 1){
xt = x - 1.0;
sign = -1;
xtpow = 1.0;
result = 0;
for(i = 1 ; i < N + 1; i++ );
{
// Problem here
printf("%d\n", i);
sign = sign * (-1);
xtpow *= xt;
result += xtpow * sign / i;
}
}else if(x >= 1)
{
return -1 * ln(1.0 / x);
}
return result;
}
The problem is with my series cycle(see above). It seems like after 1 cycle variable i becomes equal N + 1, and nothing going on after it. Have you any ideas why it is so?
It seems like after 1 cycle variable i becomes equal N + 1
remove ; after for loop:
for(i = 1 ; i < N + 1; i++ );
^
Your loop continue execute without executing code in block you putted in braces { } after for loop and for loop just increments i till for loop breaks. After the loop code block (where you commented "problem is here") get executes with i = N + 1 value.
I am not sure but I have an additional doubt about conditional expressions in if(). You code pattern is something like:
if(x > 0 && x <= 1){ <-- "True for x == 1"
// loop code
}
else if(x >= 1){ <-- "True for x == 1"
// expression code here
}
So for x == 1 else code never execute. Check this code too.

Resources