Related
Why I created a duplicate thread
I created this thread after reading Longest increasing subsequence with K exceptions allowed. I realised that the person who was asking the question hadn't really understood the problem, because he was referring to a link which solves the "Longest Increasing sub-array with one change allowed" problem. So the answers he got were actually irrelevant to LIS problem.
Description of the problem
Suppose that an array A is given with length N.
Find the longest increasing sub-sequence with K exceptions allowed.
Example
1)
N=9 , K=1
A=[3,9,4,5,8,6,1,3,7]
Answer: 7
Explanation:
Longest increasing subsequence is : 3,4,5,8(or 6),1(exception),3,7 -> total=7
N=11 , K=2
A=[5,6,4,7,3,9,2,5,1,8,7]
answer: 8
What I have done so far...
If K=1 then only one exception is allowed. If the known algorithm for computing the Longest Increasing Subsequence in O(NlogN) is used (click here to see this algorithm), then we can compute the LIS starting from A[0] to A[N-1] for each element of array A. We save the results in a new array L with size N. Looking into example n.1 the L array would be:
L=[1,2,2,3,4,4,4,4,5].
Using the reverse logic, we compute array R, each element of which contains the current Longest Decreasing Sequence from N-1 to 0.
The LIS with one exception is just sol=max(sol,L[i]+R[i+1]),
where sol is initialized as sol=L[N-1].
So we compute LIS from 0 until an index i (exception), then stop and start a new LIS until N-1.
A=[3,9,4,5,8,6,1,3,7]
L=[1,2,2,3,4,4,4,4,5]
R=[5,4,4,3,3,3,3,2,1]
Sol = 7
-> step by step explanation:
init: sol = L[N]= 5
i=0 : sol = max(sol,1+4) = 5
i=1 : sol = max(sol,2+4) = 6
i=2 : sol = max(sol,2+3) = 6
i=3 : sol = max(sol,3+3) = 6
i=4 : sol = max(sol,4+3) = 7
i=4 : sol = max(sol,4+3) = 7
i=4 : sol = max(sol,4+2) = 7
i=5 : sol = max(sol,4+1) = 7
Complexity :
O( NlogN + NlogN + N ) = O(NlogN)
because arrays R, L need NlogN time to compute and we also need Θ(N) in order to find sol.
Code for k=1 problem
#include <stdio.h>
#include <vector>
std::vector<int> ends;
int index_search(int value, int asc) {
int l = -1;
int r = ends.size() - 1;
while (r - l > 1) {
int m = (r + l) / 2;
if (asc && ends[m] >= value)
r = m;
else if (asc && ends[m] < value)
l = m;
else if (!asc && ends[m] <= value)
r = m;
else
l = m;
}
return r;
}
int main(void) {
int n, *S, *A, *B, i, length, idx, max;
scanf("%d",&n);
S = new int[n];
L = new int[n];
R = new int[n];
for (i=0; i<n; i++) {
scanf("%d",&S[i]);
}
ends.push_back(S[0]);
length = 1;
L[0] = length;
for (i=1; i<n; i++) {
if (S[i] < ends[0]) {
ends[0] = S[i];
}
else if (S[i] > ends[length-1]) {
length++;
ends.push_back(S[i]);
}
else {
idx = index_search(S[i],1);
ends[idx] = S[i];
}
L[i] = length;
}
ends.clear();
ends.push_back(S[n-1]);
length = 1;
R[n-1] = length;
for (i=n-2; i>=0; i--) {
if (S[i] > ends[0]) {
ends[0] = S[i];
}
else if (S[i] < ends[length-1]) {
length++;
ends.push_back(S[i]);
}
else {
idx = index_search(S[i],0);
ends[idx] = S[i];
}
R[i] = length;
}
max = A[n-1];
for (i=0; i<n-1; i++) {
max = std::max(max,(L[i]+R[i+1]));
}
printf("%d\n",max);
return 0;
}
Generalization to K exceptions
I have provided an algorithm for K=1. I have no clue how to change the above algorithm to work for K exceptions. I would be glad if someone could help me.
This answer is modified from my answer to a similar question at Computer Science Stackexchange.
The LIS problem with at most k exceptions admits a O(n log² n) algorithm using Lagrangian relaxation. When k is larger than log n this improves asymptotically on the O(nk log n) DP, which we will also briefly explain.
Let DP[a][b] denote the length of the longest increasing subsequence with at most b exceptions (positions where the previous integer is larger than the next one) ending at element b a. This DP is not involved in the algorithm, but defining it makes proving the algorithm easier.
For convenience we will assume that all elements are distinct and that the last element in the array is its maximum. Note that this does not limit us, as we can just add m / 2n to the mth appearance of every number, and append infinity to the array and subtract one from the answer. Let V be the permutation for which 1 <= V[i] <= n is the value of the ith element.
To solve the problem in O(nk log n), we maintain the invariant that DP[a][b] has been calculated for b < j. Loop j from 0 to k, at the jth iteration calculating DP[a][j] for all a. To do this, loop i from 1 to n. We maintain the maximum of DP[x][j-1] over x < i and a prefix maximum data structure that at index i will have DP[x][j] at position V[x] for x < i, and 0 at every other position.
We have DP[i][j] = 1 + max(DP[i'][j], DP[x][j-1]) where we go over i', x < i, V[i'] < V[i]. The prefix maximum of DP[x][j-1] gives us the maximum of terms of the second type, and querying the prefix maximum data structure for prefix [0, V[i]] gives us the maximum of terms of the first type. Then update the prefix maximum and prefix maximum data structure.
Here is a C++ implementation of the algorithm. Note that this implementation does not assume that the last element of the array is its maximum, or that the array contains no duplicates.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// Fenwick tree for prefix maximum queries
class Fenwick {
private:
vector<int> val;
public:
Fenwick(int n) : val(n+1, 0) {}
// Sets value at position i to maximum of its current value and
void inc(int i, int v) {
for (++i; i < val.size(); i += i & -i) val[i] = max(val[i], v);
}
// Calculates prefix maximum up to index i
int get(int i) {
int res = 0;
for (++i; i > 0; i -= i & -i) res = max(res, val[i]);
return res;
}
};
// Binary searches index of v from sorted vector
int bins(const vector<int>& vec, int v) {
int low = 0;
int high = (int)vec.size() - 1;
while(low != high) {
int mid = (low + high) / 2;
if (vec[mid] < v) low = mid + 1;
else high = mid;
}
return low;
}
// Compresses the range of values to [0, m), and returns m
int compress(vector<int>& vec) {
vector<int> ord = vec;
sort(ord.begin(), ord.end());
ord.erase(unique(ord.begin(), ord.end()), ord.end());
for (int& v : vec) v = bins(ord, v);
return ord.size();
}
// Returns length of longest strictly increasing subsequence with at most k exceptions
int lisExc(int k, vector<int> vec) {
int n = vec.size();
int m = compress(vec);
vector<int> dp(n, 0);
for (int j = 0;; ++j) {
Fenwick fenw(m+1); // longest subsequence with at most j exceptions ending at this value
int max_exc = 0; // longest subsequence with at most j-1 exceptions ending before this
for (int i = 0; i < n; ++i) {
int off = 1 + max(max_exc, fenw.get(vec[i]));
max_exc = max(max_exc, dp[i]);
dp[i] = off;
fenw.inc(vec[i]+1, off);
}
if (j == k) return fenw.get(m);
}
}
int main() {
int n, k;
cin >> n >> k;
vector<int> vec(n);
for (int i = 0; i < n; ++i) cin >> vec[i];
int res = lisExc(k, vec);
cout << res << '\n';
}
Now we will return to the O(n log² n) algorithm. Select some integer 0 <= r <= n. Define DP'[a][r] = max(DP[a][b] - rb), where the maximum is taken over b, MAXB[a][r] as the maximum b such that DP'[a][r] = DP[a][b] - rb, and MINB[a][r] similarly as the minimum such b. We will show that DP[a][k] = DP'[a][r] + rk if and only if MINB[a][r] <= k <= MAXB[a][r]. Further, we will show that for any k exists an r for which this inequality holds.
Note that MINB[a][r] >= MINB[a][r'] and MAXB[a][r] >= MAXB[a][r'] if r < r', hence if we assume the two claimed results, we can do binary search for the r, trying O(log n) values. Hence we achieve complexity O(n log² n) if we can calculate DP', MINB and MAXB in O(n log n) time.
To do this, we will need a segment tree that stores tuples P[i] = (v_i, low_i, high_i), and supports the following operations:
Given a range [a, b], find the maximum value in that range (maximum v_i, a <= i <= b), and the minimum low and maximum high paired with that value in the range.
Set the value of the tuple P[i]
This is easy to implement with complexity O(log n) time per operation assuming some familiarity with segment trees. You can refer to the implementation of the algorithm below for details.
We will now show how to compute DP', MINB and MAXB in O(n log n). Fix r. Build the segment tree initially containing n+1 null values (-INF, INF, -INF). We maintain that P[V[j]] = (DP'[j], MINB[j], MAXB[j]) for j less than the current position i. Set DP'[0] = 0, MINB[0] = 0 and MAXB[0] to 0 if r > 0, otherwise to INF and P[0] = (DP'[0], MINB[0], MAXB[0]).
Loop i from 1 to n. There are two types of subsequences ending at i: those where the previous element is greater than V[i], and those where it is less than V[i]. To account for the second kind, query the segment tree in the range [0, V[i]]. Let the result be (v_1, low_1, high_1). Set off1 = (v_1 + 1, low_1, high_1). For the first kind, query the segment tree in the range [V[i], n]. Let the result be (v_2, low_2, high_2). Set off2 = (v_2 + 1 - r, low_2 + 1, high_2 + 1), where we incur the penalty of r for creating an exception.
Then we combine off1 and off2 into off. If off1.v > off2.v set off = off1, and if off2.v > off1.v set off = off2. Otherwise, set off = (off1.v, min(off1.low, off2.low), max(off1.high, off2.high)). Then set DP'[i] = off.v, MINB[i] = off.low, MAXB[i] = off.high and P[i] = off.
Since we make two segment tree queries at every i, this takes O(n log n) time in total. It is easy to prove by induction that we compute the correct values DP', MINB and MAXB.
So in short, the algorithm is:
Preprocess, modifying values so that they form a permutation, and the last value is the largest value.
Binary search for the correct r, with initial bounds 0 <= r <= n
Initialise the segment tree with null values, set DP'[0], MINB[0] and MAXB[0].
Loop from i = 1 to n, at step i
Querying ranges [0, V[i]] and [V[i], n] of the segment tree,
calculating DP'[i], MINB[i] and MAXB[i] based on those queries, and
setting the value at position V[i] in the segment tree to the tuple (DP'[i], MINB[i], MAXB[i]).
If MINB[n][r] <= k <= MAXB[n][r], return DP'[n][r] + kr - 1.
Otherwise, if MAXB[n][r] < k, the correct r is less than the current r. If MINB[n][r] > k, the correct r is greater than the current r. Update the bounds on r and return to step 1.
Here is a C++ implementation for this algorithm. It also finds the optimal subsequence.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
using ll = long long;
const int INF = 2 * (int)1e9;
pair<ll, pair<int, int>> combine(pair<ll, pair<int, int>> le, pair<ll, pair<int, int>> ri) {
if (le.first < ri.first) swap(le, ri);
if (ri.first == le.first) {
le.second.first = min(le.second.first, ri.second.first);
le.second.second = max(le.second.second, ri.second.second);
}
return le;
}
// Specialised range maximum segment tree
class SegTree {
private:
vector<pair<ll, pair<int, int>>> seg;
int h = 1;
pair<ll, pair<int, int>> recGet(int a, int b, int i, int le, int ri) const {
if (ri <= a || b <= le) return {-INF, {INF, -INF}};
else if (a <= le && ri <= b) return seg[i];
else return combine(recGet(a, b, 2*i, le, (le+ri)/2), recGet(a, b, 2*i+1, (le+ri)/2, ri));
}
public:
SegTree(int n) {
while(h < n) h *= 2;
seg.resize(2*h, {-INF, {INF, -INF}});
}
void set(int i, pair<ll, pair<int, int>> off) {
seg[i+h] = combine(seg[i+h], off);
for (i += h; i > 1; i /= 2) seg[i/2] = combine(seg[i], seg[i^1]);
}
pair<ll, pair<int, int>> get(int a, int b) const {
return recGet(a, b+1, 1, 0, h);
}
};
// Binary searches index of v from sorted vector
int bins(const vector<int>& vec, int v) {
int low = 0;
int high = (int)vec.size() - 1;
while(low != high) {
int mid = (low + high) / 2;
if (vec[mid] < v) low = mid + 1;
else high = mid;
}
return low;
}
// Finds longest strictly increasing subsequence with at most k exceptions in O(n log^2 n)
vector<int> lisExc(int k, vector<int> vec) {
// Compress values
vector<int> ord = vec;
sort(ord.begin(), ord.end());
ord.erase(unique(ord.begin(), ord.end()), ord.end());
for (auto& v : vec) v = bins(ord, v) + 1;
// Binary search lambda
int n = vec.size();
int m = ord.size() + 1;
int lambda_0 = 0;
int lambda_1 = n;
while(true) {
int lambda = (lambda_0 + lambda_1) / 2;
SegTree seg(m);
if (lambda > 0) seg.set(0, {0, {0, 0}});
else seg.set(0, {0, {0, INF}});
// Calculate DP
vector<pair<ll, pair<int, int>>> dp(n);
for (int i = 0; i < n; ++i) {
auto off0 = seg.get(0, vec[i]-1); // previous < this
off0.first += 1;
auto off1 = seg.get(vec[i], m-1); // previous >= this
off1.first += 1 - lambda;
off1.second.first += 1;
off1.second.second += 1;
dp[i] = combine(off0, off1);
seg.set(vec[i], dp[i]);
}
// Is min_b <= k <= max_b?
auto off = seg.get(0, m-1);
if (off.second.second < k) {
lambda_1 = lambda - 1;
} else if (off.second.first > k) {
lambda_0 = lambda + 1;
} else {
// Construct solution
ll r = off.first + 1;
int v = m;
int b = k;
vector<int> res;
for (int i = n-1; i >= 0; --i) {
if (vec[i] < v) {
if (r == dp[i].first + 1 && dp[i].second.first <= b && b <= dp[i].second.second) {
res.push_back(i);
r -= 1;
v = vec[i];
}
} else {
if (r == dp[i].first + 1 - lambda && dp[i].second.first <= b-1 && b-1 <= dp[i].second.second) {
res.push_back(i);
r -= 1 - lambda;
v = vec[i];
--b;
}
}
}
reverse(res.begin(), res.end());
return res;
}
}
}
int main() {
int n, k;
cin >> n >> k;
vector<int> vec(n);
for (int i = 0; i < n; ++i) cin >> vec[i];
vector<int> ans = lisExc(k, vec);
for (auto i : ans) cout << i+1 << ' ';
cout << '\n';
}
We will now prove the two claims. We wish to prove that
DP'[a][r] = DP[a][b] - rb if and only if MINB[a][r] <= b <= MAXB[a][r]
For all a, k there exists an integer r, 0 <= r <= n, such that MINB[a][r] <= k <= MAXB[a][r]
Both of these follow from the concavity of the problem. Concavity means that DP[a][k+2] - DP[a][k+1] <= DP[a][k+1] - DP[a][k] for all a, k. This is intuitive: the more exceptions we are allowed to make, the less allowing one more helps us.
Fix a and r. Set f(b) = DP[a][b] - rb, and d(b) = f(b+1) - f(b). We have d(k+1) <= d(k) from the concavity of the problem. Assume x < y and f(x) = f(y) >= f(i) for all i. Hence d(x) <= 0, thus d(i) <= 0 for i in [x, y). But f(y) = f(x) + d(x) + d(x + 1) + ... + d(y - 1), hence d(i) = 0 for i in [x, y). Hence f(y) = f(x) = f(i) for i in [x, y]. This proves the first claim.
To prove the second, set r = DP[a][k+1] - DP[a][k] and define f, d as previously. Then d(k) = 0, hence d(i) >= 0 for i < k and d(i) <= 0 for i > k, hence f(k) is maximal as desired.
Proving concavity is more difficult. For a proof, see my answer at cs.stackexchange.
I want to convert the following c code to haskell code, without using lists. It returns the number of occurrences of two numbers for a given n , where n satisfies n=(a*a)*(b*b*b).
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
int main(void) {
int n = 46656;
int i,j,counter=0,res=1;
int tr = sqrt(n);
for(i=1; i<=tr; i++) {
for(j=1; j<=tr; j++) {
res = (i*i) * (j*j*j) ;
if(res==n) {
counter=counter+1;
}
printf("%d\n",res);
}
}
printf("%d\n",counter);
}
I've managed to do something similar in haskell in regarding to loops, but only for finding the overall sum. I find difficult implementing the if part and counter part(see on c code) in haskell also. Any help much appreciated! Heres my haskell code also:
sumF :: (Int->Int)->Int->Int
sumF f 0 = 0
sumF f n = sumF f (n-1) + f n
sumF1n1n :: (Int->Int->Int)->Int->Int
sumF1n1n f 0 = 0
sumF1n1n f n = sumF1n1n f (n-1)
+sumF (\i -> f i n) (n-1)
+sumF (\j -> f n j) (n-1)
+f n n
func :: Int->Int->Int
func 0 0 = 0
func a b = res
where
res = (a^2 * b^3)
call :: Int->Int
call n = sumF1n1n func n
I guess an idiomatic translation would look like this:
n = 46656
tr = sqrt n
counter = length
[ ()
| i <- [1..tr]
, j <- [1..tr]
, i*i*j*j*j == n
]
Not that it isn't possible, but definitely not the best looking:
counter n = go (sqrt n) (sqrt n)
where
go 0 _ = 0
go i tr = (go2 tr 0 i) + (go (i - 1) tr)
go2 0 c i = c
go2 j c i = go2 (j - 1) (if i^2 * j^3 == n then c + 1 else c) i
A general and relatively straightforward way to translate imperative code is to replace each basic block with a function, and give it a parameter for every piece of state it uses. If it’s a loop, it will repeatedly tail-call itself with different values of those parameters. If you don’t care about printing the intermediate results, this translates straightforwardly:
The main program prints the result of the outer loop, which begins with i = 1 and counter = 0.
main = print (outer 1 0)
where
These are constants, so we can just bind them outside the loops:
n = 46656
tr = floor (sqrt n)
The outer loop tail-calls itself with increasing i, and counter updated by the inner loop, until i > tr, then it returns the final counter.
outer i counter
| i <= tr = outer (i + 1) (inner 1 counter)
| otherwise = counter
where
The inner loop tail-calls itself with increasing j, and its counter (counter') incremented when i^2 * j^3 == n, until j > tr, then it returns the updated counter back to outer. Note that this is inside the where clause of outer because it uses i to calculate res—you could alternatively make i an additional parameter.
inner j counter'
| j <= tr = inner (j + 1) $ let
res = i ^ 2 * j ^ 3
in if res == n then counter' + 1 else counter'
| otherwise = counter'
If I have an array, for example this
Dim Players() As String = {"Adam", "Helen", "Jack", "Emily"}
How do I change the position of each element by a value, for example if by 1, Adam will move to 2, Helen will move to 3, and Emily should come back to 1.
I was able to do this if increment by 1. I would have the last element stored in a variable and when done moving all elements by 1, I would set the first element to the variable(Emily in this case).
How would I do it if having different increment values like 2, 3, 4, or even 6?
Edit: Any programming language is fine but it has to be done with for loops and no functions.
Update: I am an IGCSE student and this question is bugging from over a week.
If you can do an increment of one, just repeat that the required number of times.
A better way would be: (let's call the increment n); store the end n elements in another array; move the elements before that towards the end of the array; copy back the elements from the other array to the start. Like this:
Module Module1
Sub Main()
Dim players() As String = {"Adam", "Becky", "Clive", "Debby", "Edward", "Fiona"}
Dim rotateBy = 2
' make sure shiftBy is in a usable range
rotateBy = rotateBy Mod players.Length
' if shiftBy is negative, make it positive such that a left-rotate is performed
If rotateBy < 0 Then
rotateBy = rotateBy + players.Length
End If
' store the elements which will be moved to the other end of the array
Dim tmp(rotateBy - 1) As String
Dim startIndex = players.Length - rotateBy
For i = startIndex To players.Length - 1
tmp(i - startIndex) = players(i)
Next
' move the elements
For i = players.Length - 1 - rotateBy To 0 Step -1
players(i + rotateBy) = players(i)
Next
'fill in the other elements
For i = 0 To rotateBy - 1
players(i) = tmp(i)
Next
' show the result
Console.WriteLine(String.Join(", ", players))
Console.ReadLine()
End Sub
End Module
Note that the copying of elements within the players array is done backwards so that an overlapping range does not splat over values which have yet to be moved.
To shift the elements backwards, use a negative value for rotateBy.
If you are using a language with a function which gives array copying functions, this method is easy to adapt to use that functionality. For anyone wondering about that in the future:
Module Module1
Sub Main()
Dim players() As String = {"Adam", "Becky", "Clive", "Debby", "Edward", "Fiona"}
Dim rotateBy = 4
' make sure shiftBy is in a usable range
rotateBy = rotateBy Mod players.Length
' if shiftBy is negative, make it positive such that a left-rotate is performed
If rotateBy < 0 Then
rotateBy = rotateBy + players.Length
End If
' store the elements which will be moved to the other end of the array
Dim tmp(rotateBy - 1) As String
Dim startIndex = players.Length - rotateBy
Array.Copy(players, startIndex, tmp, 0, rotateBy)
Array.Copy(players, 0, players, rotateBy, startIndex)
Array.Copy(tmp, players, tmp.Length)
' show the result
Console.WriteLine(String.Join(", ", players))
Console.ReadLine()
End Sub
End Module
As you mentioned any programming language, this is what it could be in C# as a function but without using any framework methods such as Array.Copy:
public static void RotateUsingLoops<T>(T[] elements, int rotateBy)
{
rotateBy = rotateBy % elements.Length;
if (rotateBy < 0)
{
rotateBy += elements.Length;
}
T[] tmp = new T[rotateBy];
int startIndex = elements.Length - rotateBy;
// store the elements which will be moved to the other end of the array
for (int i = startIndex; i < elements.Length; i++)
{
tmp[i - startIndex] = elements[i];
}
// move the elements
for (int i = elements.Length - 1 - rotateBy; i >= 0; i--)
{
elements[i + rotateBy] = elements[i];
}
// fill in the other elements
for (int i = 0; i < rotateBy; i++)
{
elements[i] = tmp[i];
}
}
Whereby you could use
static void Main(string[] args)
{
var a = new string[] { "A", "B", "C", "D" };
RotateUsingLoops(a, 2);
Console.WriteLine(string.Join(", ", a));
Console.ReadLine();
}
to get an output of:
C, D, A, B
Circular array rotation is what you are referring towards.
In VB.NET the following code will rotate an array of elements to the Right, by the Shift amount.
Sub Main()
Dim Players() As String = {"Adam", "Helen", "Jack", "Emily"}
Dim Players_Shifted(Players.Length - 1) As String
Dim Shift As Integer = 2
If Shift > Players.Length Then
Shift = Shift Mod Players.Length
End If
For index = 0 To Shift - 1
Players_Shifted(index) = Players(Players.Length - Shift + index)
Next
Dim index_2 = 0
For index = Shift To Players.Length - 1
Players_Shifted(index) = Players(index_2)
index_2 = index_2 + 1
Next
Players = Players_Shifted
' Print out Players Array '
For index = 0 To Players.Length - 1
Console.Write(Players(index) + ", ")
Next
Console.ReadLine()
End Sub
Here is a snippet of a C++ code that implements this functionality-
Let temp be a temporary variable, N be the size of the array, shift be the number of places u wanna shift and arr be the original array.
int temp = arr[0];
int i = 0;
while(True){
//swap(arr(i + shift)%N, temp);
swap_var = temp;
temp = arr[(i+shift)%N];
arr[(i+shift)%N] = swap_var;
i = (i+shift)%N;
if (i == 0){
break;
}
}
Edit: Using for loop.
int temp = arr[0]; //change int to the type of variable ur array is
int i =0;
for (int count = 0; count<N ; count++){
//swap(arr(i + shift)%N, temp);
swap_var = temp;
temp = arr[(i+shift)%N];
arr[(i+shift)%N] = swap_var;
i = (i+shift)%N;
}
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
I am trying to generate random data based on some criteria. I am stuck on how to start so I don't have any code.
I just want some guide on how I can achieve that. You need not provide complete code.
So, coming to the problem, lets say we have this existing data:
Total - 10
Won - 7
Lost - 3
Longest Winning Streak - 5
Longest Losing Streak - 2
Now I need to generate an array of random boolean values (true representing a win and false representing a loss) which fulfills the above criteria.
So, in this case the output can be any of the following:
0011011111
1111101100
1010011111
..........
Its the streak part that bothers me. If it weren't for the streak, I could have generated seven 1(s) and three 0(s) and then randomly shuffled them.
Note: I would prefer solutions in C#, VB.NET, JavaScript, or Python but any language is welcome.
I'd suggest a genetic algorithm.
class Program
{
static int len = 10;
static int win = 7;
static int lws = 5;
static int lls = 2;
static Random rnd = new Random();
static void Main(string[] args)
{
int genSz = 15;
var generation = new List<Chromosome>();
Helper.Repeat(() => generation.Add(new Chromosome()), genSz);
int gen = 1;
while (generation.First().Fitness != 0)
{
//procreation
Helper.Repeat(() => {
int x1 = rnd.Next(genSz / 2);
int x2 = rnd.Next(genSz);
generation.Add(new Chromosome(generation[x1], generation[x2]));
}, genSz);
//selection
generation = generation.OrderBy(x => x.Fitness).Take(genSz).ToList();
Console.WriteLine("GENERATION " + gen++);
foreach (var x in generation)
{
Console.WriteLine(x);
}
Console.ReadLine();
}
Console.ReadLine();
}
class Chromosome
{
bool[] genes = new bool[len];
public Chromosome() { }
public Chromosome(Chromosome p1, Chromosome p2)
{
//crossingover
rnd.Shuffle(ref p1, ref p2); //may reorder parents or not
var x = rnd.Next(len);
Array.Copy(p1.genes, 0, genes, 0, x);
Array.Copy(p2.genes, x, genes, x, len - x);
//mutation
if (rnd.Flip())
{
x = rnd.Next(len);
genes[x] = !genes[x];
}
}
public int Fitness
{
get
{
int w = genes.Count(g => g);
int l = len - w;
int ws = genes.LongestStreak(g => g);
int ls = genes.LongestStreak(g => !g);
return Math.Abs(w - win) + Math.Abs(lws - ws) + Math.Abs(lls - ls);
}
}
public override string ToString()
{
return "[" + new string(genes.Select(g => g ? '*' : '.').ToArray()) + "] " + Fitness.ToString();
}
}
}
public static class Helper
{
public static bool Flip(this Random rnd) => rnd.Next(2) == 0;
public static void Shuffle<T>(this Random rnd, ref T a, ref T b, bool allowNoChange = true)
{
if (allowNoChange && rnd.Flip()) return; //no reordering
T tmp = a; a = b; b = tmp;
}
public static int LongestStreak<T>(this IEnumerable<T> sequence, Predicate<T> selector)
{
int current = 0;
int longest = 0;
foreach (T x in sequence)
{
if (selector(x))
{
current++;
if (current > longest) longest = current;
}
else
{
current = 0;
}
}
return longest;
}
public static void Repeat(this Action action, int N)
{
for (int n = 0; n < N; n++) action();
}
}
Second variant - brute force. Can be used if the sequence is short. Also you can get all possible variants with it.
class Program
{
static void Main(string[] args)
{
var res = new[] { true, false }.Words(10).Where(w => {
return
w.Count(g => g) == 7 &&
w.LongestStreak(g => g) == 5 &&
w.LongestStreak(g => !g) == 2;
});
foreach (var v in res)
{
foreach (var b in v)
{
Console.Write(b ? "*" : ".");
}
Console.WriteLine();
}
Console.WriteLine(res.Count());
Console.ReadLine();
}
}
public static class Helper
{
public static IEnumerable<IEnumerable<T>> Words<T>(this IEnumerable<T> alphabet, int len)
{
foreach (var l in alphabet)
{
if (len == 1)
{
yield return l.Yield();
}
else
{
foreach (var w in alphabet.Words(len - 1))
{
yield return w.Prepend(l);
}
}
}
}
public static IEnumerable<T> Yield<T>(this T item)
{
yield return item;
}
static IEnumerable<T> Prepend<T>(this IEnumerable<T> rest, T first)
{
yield return first;
foreach (var item in rest)
yield return item;
}
public static int LongestStreak<T>(this IEnumerable<T> sequence, Predicate<T> selector)
{
int current = 0;
int longest = 0;
foreach (T x in sequence)
{
if (selector(x))
{
current++;
if (current > longest) longest = current;
}
else
{
current = 0;
}
}
return longest;
}
}
My suggestion would be to use an algorithm to select k bits (your won number) from a length-n (your total number) string. Here, I use the kbits(n, k) function written by #foglebird. You can then filter out the undesired permutations using a list comprehension.
import itertools
def kbits(n, k):
result = []
for bits in itertools.combinations(range(n), k):
s = ['0'] * n
for bit in bits:
s[bit] = '1'
result.append(''.join(s))
return result
total = 10
won = 7
lost = 3
max_win = 5
max_lose = 2
answer = [x for x in kbits(total, won) if '1'*(max_win+1) not in x and '0'*(max_lose+1) not in x]
I had an answer posted, then noticed I was missed some key requirements. I added and changed some stuff to address those missing elements.
The core method fails most of the time, but it does so quickly enough that you can do it in a loop until you get a good answer. Depending on the actual values, in cases where there are very few legal results, it seems like you need to luck out.
The steps used:
Pick a random spot for the longest streak (Win in the example)
Bracket the streak with losses to prevent extending it when setting leftovers
Find the indices with enough consecutive slots to hold the loss streak
Pick a random one and set the loss streak (returns if there are none)
Set all the Leftovers as Not the value at n-1 to avoid extending or creating a new streak
So, it becomes hit or miss whether then WinCount and LossCount are correct. That seems easier to stumble upon than streaks of the right size. A wrapper method tests the result to reject and rerun. With the given values, it usually find a winner in the first 10 or so times.
The core method to construct a string representation, and a helper:
' ToDo change to return Bool() = string is easier to read
Private Function FarhamStreaks(winStrk As Int32, loseStrk As Int32, total As Int32) As String
' -1 == not set
Dim result = Enumerable.Repeat(-1, total).ToArray
' set longest streak first
Dim wNDX = RNG.Next(0, total + 1 - winStrk)
For n As Int32 = 0 To winStrk - 1
result(wNDX + n) = 1
Next
' bracket with losers so the w streak cant extend
If wNDX > 0 Then result(wNDX - 1) = 0
If wNDX + winStrk < result.Length - 1 Then result(wNDX + winStrk) = 0
' look for eligible consecutive starting slots
' might be none
Dim lossNdx As New List(Of Int32)
For n As Int32 = 0 To result.Count - 1
Dim count = CountConsecutiveLooserSlotsFrom(n, result)
If (n + 1) < result.Count AndAlso count >= loseStrk Then
lossNdx.Add(n)
End If
Next
If lossNdx.Count = 0 Then
' do over
' the code has never gotten here
' but depends on the mix of values
Return ""
End If
' set losses
Dim lNdx = lossNdx(RNG.Next(0, lossNdx.Count))
For n As Int32 = 0 To loseStrk - 1
result(lNdx + n) = 0
Next
' set the leftovers based on next value to avoid
' extending streaks
For n As Int32 = 0 To result.Length - 1
If result(n) = -1 Then
If n > 0 Then
result(n) = If(result(n - 1) = 0, 1, 0)
Else
result(n) = If(result(n + 1) = 0, 1, 0)
End If
End If
Next
Dim resultString = String.Join(",", result)
' convert to boolean
Dim realResult(total) As Boolean
For n As Int32 = 0 To total - 1
realResult(n) = Convert.ToBoolean(result(n))
Next
Return resultString
End Function
' find candidate slots for the shorter streak:
Private Function CountConsecutiveLooserSlotsFrom(ndx As Integer, theArray As Int32()) As Int32
Dim count As Int32 = 1 ' including ndx
For n As Int32 = ndx To theArray.Length - 2
If theArray(n) <> 1 AndAlso theArray(n + 1) <> 1 Then
count += 1
Else
Exit For
End If
Next
Return count
End Function
The method to validate a result candidate (and performance metrics):
Private Function MakeFarhamStreak(wins As Int32, winStreak As Int32,
lossStreak As Int32,
total As Int32) As String
Const MaxTries As Int32 = 999
Dim losses = (total - wins)
Dim reverse As Boolean = (lossStreak > winStreak)
Dim candidate As String
Dim sw As New Stopwatch
Dim pass, fail As Int32
Dim count As Int32
sw.Start()
For n As Int32 = 0 To MaxTries
If reverse Then
candidate = FarhamStreaks(lossStreak, winStreak, total)
' to do: un-reverse (Not) the results -
Else
candidate = FarhamStreaks(winStreak, lossStreak, total)
End If
Dim result = candidate.Split(","c)
' test win count
count = candidate.Where(Function(f) f = "1").Count
If count <> wins Then
fail += 1
Continue For
End If
' test loss count
count = candidate.Where(Function(f) f = "0").Count
If count <> losses Then
fail += 1
Continue For
End If
Dim tmp = candidate.Replace(","c, "")
' test win streak size
Dim wstreaks = tmp.Select(Function(c, i) tmp.Substring(i).
TakeWhile(Function(q) q = c AndAlso q = "1").
Count()).
Max
If wstreaks <> winStreak Then
fail += 1
Continue For
End If
Dim lstreaks = tmp.Select(Function(c, i) tmp.Substring(i).
TakeWhile(Function(q) q = c AndAlso q = "0").
Count()).
Max
If lstreaks <> lossStreak Then
fail += 1
Continue For
End If
pass += 1
If pass = 1 Then
Console.WriteLine("First Pass in {0}ms (try # {1} = {2})",
sw.ElapsedMilliseconds, n, candidate)
' normally, return at this point
End If
Next
End Function
It is easier to fit the shorter streak around the longer one, so it reverses the parm order as needed. There isnt code to flip/Not the results.
results:
First Pass in 18ms (try # 4 = 1,1,1,1,1,0,0,1,0,1)
Total FAILURES 753 75.38%
Total Pass 247 24.72%
Total time for 999 candidates 29ms
It found the first passing value on try #4 - with the 10, 7w, 5ws, 2ls values it usually finds one in the first 10.
This is a interview question: given an array of integers find the max. and min. using minimum comparisons.
Obviously, I can loop over the array twice and use ~2n comparisons in the worst case but I would like to do better.
1. Pick 2 elements(a, b), compare them. (say a > b)
2. Update min by comparing (min, b)
3. Update max by comparing (max, a)
This way you would do 3 comparisons for 2 elements, amounting to 3N/2 total comparisons for N elements.
Trying to improve on the answer by srbh.kmr. Say we have the sequence:
A = [a1, a2, a3, a4, a5]
Compare a1 & a2 and calculate min12, max12:
if (a1 > a2)
min12 = a2
max12 = a1
else
min12 = a1
max12 = a2
Similarly calculate min34, max34. Since a5 is alone, keep it as it is...
Now compare min12 & min34 and calculate min14, similarly calculate max14. Finally compare min14 & a5 to calculate min15. Similarly calculate max15.
Altogether it's only 6 comparisons!
This solution can be extended to an array of arbitrary length. Probably can be implemented by a similar approach to merge-sort (break the array in half and calculate min max for each half).
UPDATE: Here's the recursive code in C:
#include <stdio.h>
void minmax (int* a, int i, int j, int* min, int* max) {
int lmin, lmax, rmin, rmax, mid;
if (i == j) {
*min = a[i];
*max = a[j];
} else if (j == i + 1) {
if (a[i] > a[j]) {
*min = a[j];
*max = a[i];
} else {
*min = a[i];
*max = a[j];
}
} else {
mid = (i + j) / 2;
minmax(a, i, mid, &lmin, &lmax);
minmax(a, mid + 1, j, &rmin, &rmax);
*min = (lmin > rmin) ? rmin : lmin;
*max = (lmax > rmax) ? lmax : rmax;
}
}
void main () {
int a [] = {3, 4, 2, 6, 8, 1, 9, 12, 15, 11};
int min, max;
minmax (a, 0, 9, &min, &max);
printf ("Min : %d, Max: %d\n", min, max);
}
Now I cannot make out the exact number of comparisons in terms of N (the number of elements in the array). But it's hard to see how one can go below this many comparisons.
UPDATE: We can work out the number of comparisons like below:
At the bottom of this tree of computations, we form pairs of integers from the original array. So we have N / 2 leaf nodes. For each of these leaf nodes we do exactly 1 comparison.
By referring to the properties of a perfect-binary-tree, we have:
leaf nodes (L) = N / 2 // known
total nodes (n) = 2L - 1 = N - 1
internal nodes = n - L = N / 2 - 1
For each internal node we do 2 comparisons. Therefore, we have N - 2 comparisons. Along with the N / 2 comparisons at the leaf nodes, we have (3N / 2) - 2 total comparisons.
So, may be this is the solution srbh.kmr implied in his answer.
A somewhat different approach, which uses integer arithmetic instead of comparisons (which wasn't explicitly prohibited)
for(int i=0;i<N;i++) {
xmin += x[i]-xmin & x[i]-xmin>>31;
xmax += x[i]-xmax & xmax-x[i]>>31;
}
go for divide and conquer !
1,3,2,5
for this finding min,max will take 6 comparisons
but divide them
1,3 ---> will give min 1 and max 3 in one comparison
2,5 ---> will give min 2 and max 5 in one comparison
now we can compare two mins and maxs
min(1,2) --> will give the final min as 1 (one comparison)
max(3,5) ---> will give the final max as 5 (one comparison)
so totally four comparisons to find both min and max.
After reading the question and answers, I decided to try a few versions (in C#).
I thought the fastest would be Anton Knyazyev's one (branch free),
it isn't (on my box).
Results:
/* comp. time(ns)
minmax0 3n/2 855
minmax1 2n 805
minmax2 2n 1315
minmax3 2n 685 */
Why are minmax1 and minmax3 faster?
Probably because the "branch predictor" does a nice job,
each iteration the chance, a new min (or max) is found, decreases,
so predictions become better.
All in all it's a simple test. I do realize my conclusions may be:
-premature.
-not valid for different platforms.
Let's say they're indicative.
Edit: Break-even point minmax0, minmax3: ~100 items,
10,000 items: minmax3 ~3.5 times faster than minmax0.
using System;
using sw = System.Diagnostics.Stopwatch;
class Program
{
static void Main()
{
int n = 1000;
int[] a = buildA(n);
sw sw = new sw();
sw.Start();
for (int i = 1000000; i > 0; i--) minMax3(a);
sw.Stop();
Console.Write(sw.ElapsedMilliseconds);
Console.Read();
}
static int[] minMax0(int[] a) // ~3j/2 comp.
{
int j = a.Length - 1;
if (j < 2) return j < 0 ? null :
j < 1 ? new int[] { a[0], a[0] } :
a[0] < a[1] ? new int[] { a[0], a[1] } :
new int[] { a[1], a[0] };
int a0 = a[0], a1 = a[1], ai = a0;
if (a1 < a0) { a0 = a1; a1 = ai; }
int i = 2;
for (int aj; i < j; i += 2)
{
if ((ai = a[i]) < (aj = a[i + 1])) // hard to predict
{ if (ai < a0) a0 = ai; if (aj > a1) a1 = aj; }
else
{ if (aj < a0) a0 = aj; if (ai > a1) a1 = ai; }
}
if (i <= j)
{ if ((ai = a[i]) < a0) a0 = ai; else if (ai > a1) a1 = ai; }
return new int[] { a0, a1 };
}
static int[] minMax1(int[] a) // ~2j comp.
{
int j = a.Length;
if (j < 3) return j < 1 ? null :
j < 2 ? new int[] { a[0], a[0] } :
a[0] < a[1] ? new int[] { a[0], a[1] } :
new int[] { a[1], a[0] };
int a0 = a[0], a1 = a0, ai = a0;
for (int i = 1; i < j; i++)
{
if ((ai = a[i]) < a0) a0 = ai;
else if (ai > a1) a1 = ai;
}
return new int[] { a0, a1 };
}
static int[] minMax2(int[] a) // ~2j comp.
{
int j = a.Length;
if (j < 2) return j == 0 ? null : new int[] { a[0], a[0] };
int a0 = a[0], a1 = a0;
for (int i = 1, ai = a[1], aj = ai; ; aj = ai = a[i])
{
ai -= a0; a0 += ai & ai >> 31;
aj -= a1; a1 += aj & -aj >> 31;
i++; if (i >= j) break;
}
return new int[] { a0, a1 };
}
static int[] minMax3(int[] a) // ~2j comp.
{
int j = a.Length - 1;
if (j < 2) return j < 0 ? null :
j < 1 ? new int[] { a[0], a[0] } :
a[0] < a[1] ? new int[] { a[0], a[1] } :
new int[] { a[1], a[0] };
int a0 = a[0], a1 = a[1], ai = a0;
if (a1 < a0) { a0 = a1; a1 = ai; }
int i = 2;
for (j -= 2; i < j; i += 3)
{
ai = a[i + 0]; if (ai < a0) a0 = ai; if (ai > a1) a1 = ai;
ai = a[i + 1]; if (ai < a0) a0 = ai; if (ai > a1) a1 = ai;
ai = a[i + 2]; if (ai < a0) a0 = ai; if (ai > a1) a1 = ai;
}
for (j += 2; i <= j; i++)
{ if ((ai = a[i]) < a0) a0 = ai; else if (ai > a1) a1 = ai; }
return new int[] { a0, a1 };
}
static int[] buildA(int n)
{
int[] a = new int[n--]; Random rand = new Random(0);
for (int j = n; n >= 0; n--) a[n] = rand.Next(-1 * j, 1 * j);
return a;
}
}
Brute-force is FASTER!
I would love someone to show me the error of my ways, here, but, …
I compared the actual run times of the brute-force method vs. the (more beautiful) recursive divide and conquer. Typical results (in 10,000,000 calls to each function):
Brute force :
0.657 seconds 10 values => 16 comparisons. Min # 8, Max # 10
0.604 seconds 1000000 values => 1999985 comparisons. Min # 983277, Max # 794659
Recursive :
1.879 seconds 10 values => 13 comparisons. Min # 8, Max # 10
2.041 seconds 1000000 values => 1499998 comparisons. Min # 983277, Max # 794659
Surprisingly, the brute-force method was about 2.9 times faster for an array of 10 items, and 3.4 times faster for an array of 1,000,000 items.
Evidently, the number of comparisons is not the problem, but possibly the number of re-assignments, and the overhead of calling a recursive function (I have no idea why 1,000,000 values ran faster than 10 values, but it did!).
Caveats : I did this in VBA, not C, and I was comparing double-precision numbers and returning the index into the array of the Min and Max values.
Here is the code I used (class cPerformanceCounter is not included here but uses QueryPerformanceCounter for high-resolution timing) :
Option Explicit
'2021-02-17
Private Const MIN_LONG As Long = -2147483648#
Private m_l_NumberOfComparisons As Long
Sub Time_MinMax()
Const LBOUND_VALUES As Long = 1
Dim l_pcOverall As cPerformanceCounter
Dim l_d_Values() As Double
Dim i As Long, _
k As Long, _
l_l_UBoundValues As Long, _
l_l_NumberOfIterations As Long, _
l_l_IndexOfMin As Long, _
l_l_IndexOfMax As Long
Set l_pcOverall = New cPerformanceCounter
For k = 1 To 2
l_l_UBoundValues = IIf(k = 1, 10, 1000000)
ReDim l_d_Values(LBOUND_VALUES To l_l_UBoundValues)
'Assign random values
Randomize '1 '1 => the same random values to be used each time
For i = LBOUND_VALUES To l_l_UBoundValues
l_d_Values(i) = Rnd
Next i
For i = LBOUND_VALUES To l_l_UBoundValues
l_d_Values(i) = Rnd
Next i
'This keeps the total run time in the one-second neighborhood
l_l_NumberOfIterations = 10000000 / l_l_UBoundValues
'——————— Time Brute Force Method —————————————————————————————————————————
l_pcOverall.RestartTimer
For i = 1 To l_l_NumberOfIterations
m_l_NumberOfComparisons = 0
IndexOfMinAndMaxDoubleBruteForce _
l_d_Values, _
LBOUND_VALUES, _
l_l_UBoundValues, _
l_l_IndexOfMin, _
l_l_IndexOfMax
Next
l_pcOverall.ElapsedSecondsDebugPrint _
3.3, , _
" seconds Brute-Force " & l_l_UBoundValues & " values => " _
& m_l_NumberOfComparisons & " comparisons. " _
& " Min # " & l_l_IndexOfMin _
& ", Max # " & l_l_IndexOfMax, _
True
'——————— End Time Brute Force Method —————————————————————————————————————
'——————— Time Brute Force Using Individual Calls —————————————————————————
l_pcOverall.RestartTimer
For i = 1 To l_l_NumberOfIterations
m_l_NumberOfComparisons = 0
l_l_IndexOfMin = IndexOfMinDouble(l_d_Values)
l_l_IndexOfMax = IndexOfMaxDouble(l_d_Values)
Next
l_pcOverall.ElapsedSecondsDebugPrint _
3.3, , _
" seconds Individual " & l_l_UBoundValues & " values => " _
& m_l_NumberOfComparisons & " comparisons. " _
& " Min # " & l_l_IndexOfMin _
& ", Max # " & l_l_IndexOfMax, _
True
'——————— End Time Brute Force Using Individual Calls —————————————————————
'——————— Time Recursive Divide and Conquer Method ————————————————————————
l_pcOverall.RestartTimer
For i = 1 To l_l_NumberOfIterations
m_l_NumberOfComparisons = 0
IndexOfMinAndMaxDoubleRecursiveDivideAndConquer _
l_d_Values, _
LBOUND_VALUES, _
l_l_UBoundValues, _
l_l_IndexOfMin, l_l_IndexOfMax
Next
l_pcOverall.ElapsedSecondsDebugPrint _
3.3, , _
" seconds Recursive " & l_l_UBoundValues & " values => " _
& m_l_NumberOfComparisons & " comparisons. " _
& " Min # " & l_l_IndexOfMin _
& ", Max # " & l_l_IndexOfMax, _
True
'——————— End Time Recursive Divide and Conquer Method ————————————————————
Next k
End Sub
'Recursive divide and conquer
Sub IndexOfMinAndMaxDoubleRecursiveDivideAndConquer( _
i_dArray() As Double, _
i_l_LBound As Long, _
i_l_UBound As Long, _
o_l_IndexOfMin As Long, _
o_l_IndexOfMax As Long)
Dim l_l_IndexOfLeftMin As Long, _
l_l_IndexOfLeftMax As Long, _
l_l_IndexOfRightMin As Long, _
l_l_IndexOfRightMax As Long, _
l_l_IndexOfMidPoint As Long
If (i_l_LBound = i_l_UBound) Then 'Only one element
o_l_IndexOfMin = i_l_LBound
o_l_IndexOfMax = i_l_LBound
ElseIf (i_l_UBound = (i_l_LBound + 1)) Then 'Only two elements
If (i_dArray(i_l_LBound) > i_dArray(i_l_UBound)) Then
o_l_IndexOfMin = i_l_UBound
o_l_IndexOfMax = i_l_LBound
Else
o_l_IndexOfMin = i_l_LBound
o_l_IndexOfMax = i_l_UBound
End If
m_l_NumberOfComparisons = m_l_NumberOfComparisons + 1
Else 'More than two elements => recurse
l_l_IndexOfMidPoint = (i_l_LBound + i_l_UBound) / 2
'Find the min of the elements in the left half
IndexOfMinAndMaxDoubleRecursiveDivideAndConquer _
i_dArray, _
i_l_LBound, _
l_l_IndexOfMidPoint, _
l_l_IndexOfLeftMin, _
l_l_IndexOfLeftMax
'Find the min of the elements in the right half
IndexOfMinAndMaxDoubleRecursiveDivideAndConquer i_dArray, _
l_l_IndexOfMidPoint + 1, _
i_l_UBound, _
l_l_IndexOfRightMin, _
l_l_IndexOfRightMax
'Return the index of the lower of the two values returned
If (i_dArray(l_l_IndexOfLeftMin) > i_dArray(l_l_IndexOfRightMin)) Then
o_l_IndexOfMin = l_l_IndexOfRightMin
Else
o_l_IndexOfMin = l_l_IndexOfLeftMin
End If
m_l_NumberOfComparisons = m_l_NumberOfComparisons + 1
'Return the index of the lower of the two values returned
If (i_dArray(l_l_IndexOfLeftMax) > i_dArray(l_l_IndexOfRightMax)) Then
o_l_IndexOfMax = l_l_IndexOfLeftMax
Else
o_l_IndexOfMax = l_l_IndexOfRightMax
End If
m_l_NumberOfComparisons = m_l_NumberOfComparisons + 1
End If
End Sub
Sub IndexOfMinAndMaxDoubleBruteForce( _
i_dArray() As Double, _
i_l_LBound As Long, _
i_l_UBound As Long, _
o_l_IndexOfMin As Long, _
o_l_IndexOfMax As Long)
Dim i As Long
o_l_IndexOfMin = i_l_LBound
o_l_IndexOfMax = o_l_IndexOfMin
For i = i_l_LBound + 1 To i_l_UBound
'Usually we will do two comparisons
m_l_NumberOfComparisons = m_l_NumberOfComparisons + 2
If (i_dArray(i) < i_dArray(o_l_IndexOfMin)) Then
o_l_IndexOfMin = i
'We don't need to do the ElseIf comparison
m_l_NumberOfComparisons = m_l_NumberOfComparisons - 1
ElseIf (i_dArray(i) > i_dArray(o_l_IndexOfMax)) Then
o_l_IndexOfMax = i
End If
Next i
End Sub
Function IndexOfMinDouble( _
i_dArray() As Double _
) As Long
Dim i As Long
On Error GoTo EWE
IndexOfMinDouble = LBound(i_dArray)
For i = IndexOfMinDouble + 1 To UBound(i_dArray)
If (i_dArray(i) < i_dArray(IndexOfMinDouble)) Then
IndexOfMinDouble = i
End If
m_l_NumberOfComparisons = m_l_NumberOfComparisons + 1
Next i
On Error GoTo 0
Exit Function
EWE:
On Error GoTo 0
IndexOfMinDouble = MIN_LONG
End Function
Function IndexOfMaxDouble( _
i_dArray() As Double _
) As Long
Dim i As Long
On Error GoTo EWE
IndexOfMaxDouble = LBound(i_dArray)
For i = IndexOfMaxDouble + 1 To UBound(i_dArray)
If (i_dArray(i) > i_dArray(IndexOfMaxDouble)) Then
IndexOfMaxDouble = i
End If
m_l_NumberOfComparisons = m_l_NumberOfComparisons + 1
Next i
On Error GoTo 0
Exit Function
EWE:
On Error GoTo 0
IndexOfMaxDouble = MIN_LONG
End Function
A simple pseudo code for the recursive algorithm:
Function MAXMIN (A, low, high)
if (high − low + 1 = 2) then
if (A[low] < A[high]) then
max = A[high]; min = A[low].
return((max, min)).
else
max = A[low]; min = A[high].
return((max, min)).
end if
else
mid = low+high/2
(max_l , min_l ) = MAXMIN(A, low, mid).
(max_r , min_r ) =MAXMIN(A, mid + 1, high).
end if
Set max to the larger of max_l and max_r ;
likewise, set min to the smaller of min_l and min_r .
return((max, min)).
import java.util.*;
class Maxmin
{
public static void main(String args[])
{
int[] arr = new int[10];
Scanner in = new Scanner(System.in);
int i, min=0, max=0;
for(i=0; i<=9; i++)
{
System.out.print("Enter any number: ");
arr[i] = in.nextInt();
}
min = arr[0];
for(i=0; i<=9; i++)
{
if(arr[i] > max)
{
max = arr[i];
}
if(arr[i] < min)
{
min = arr[i];
}
}
System.out.println("Maximum is: " + max);
System.out.println("Minimum is: " + min);
}
}
My divide & conquer approach with java so far:
public class code {
static int[] A = {444,9,8,6,199,3,0,5,3,200};
static int min = A[0], max = A[1];
static int count = 0;
public void minMax(int[] A, int i, int j) {
if(i==j) {
count = count + 2;
min = Math.min(min, A[i]);
max = Math.max(max, A[i]);
}
else if(j == i+1) {
if(A[i] > A[j]) {
count = count + 3;
min = Math.min(min, A[j]);
max = Math.max(max, A[i]);
}
else {
count = count + 3;
min = Math.min(min, A[i]);
max = Math.max(max, A[j]);
}
}
else {
minMax(A,i,(i+j)/2);
minMax(A,(i+j)/2+1,j);
}
}
public static void main(String[] args) {
code c = new code();
if(Math.min(A[0], A[1]) == A[0]) {
count++;
min = A[0];
max = A[1];
}
else {
count++;
min = A[1];
max = A[0];
}
c.minMax(A,2,A.length-1);
System.out.println("Min: "+min+" Max: "+max);
System.out.println("Total comparisons: " + count);
}
}
public static int[] minMax(int[] array){
int [] empty = {-1,-1};
if(array==null || array.length==0){
return empty;
}
int lo =0, hi = array.length-1;
return minMax(array,lo, hi);
}
private static int[] minMax(int []array, int lo, int hi){
if(lo==hi){
int [] result = {array[lo], array[hi]};
return result;
}else if(lo+1==hi){
int [] result = new int[2];
result[0] = Math.min(array[lo], array[hi]);
result[1] = Math.max(array[lo], array[hi]);
return result;
}else{
int mid = lo+(hi-lo)/2;
int [] left = minMax(array, lo, mid);
int [] right = minMax(array, mid+1, hi);
int []result = new int[2];
result[0] = Math.min(left[0], right[0]);
result[1] = Math.max(left[1], right[1]);
return result;
}
}
public static void main(String[] args) {
int []array = {1,2,3,4,100};
System.out.println("min and max values are "+Arrays.toString(minMax(array)));
}
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin>>n;
set<int> t;
for(int i=0;i<n;i++)
{
int x;
cin>>x;
t.insert(x);
}
set<int>::iterator s,b;
s=t.begin();
b=--t.end();
cout<< *s<<" "<<*b<<endl;
enter code here
return 0;
}
// this can be done in log(n) complexity!!!
if (numbers.Length <= 0)
{
Console.WriteLine("There are no elements");
return;
}
if (numbers.Length == 1)
{
Console.WriteLine($"There is only one element. So min and max of this
array is: {numbers[0]}");
return;
}
if (numbers.Length == 2)
{
if (numbers[0] > numbers[1])
{
Console.WriteLine($"min = {numbers[1]}, max = {numbers[0]}");
return;
}
Console.WriteLine($"min = {numbers[0]}, max = {numbers[1]}");
return;
}
int i = 0;
int j = numbers.Length - 1;
int min = numbers[i];
int max = numbers[j];
i++;
j--;
while (i <= j)
{
if(numbers[i] > numbers[j])
{
if (numbers[j] < min) min = numbers[j];
if (numbers[i] > max) max = numbers[i];
}
else
{
if (numbers[i] < min) min = numbers[i];
if (numbers[j] > max) max = numbers[j];
}
i++;
j--;
}
It's a solution written in C#. I find this method of burning the candle at both ends to be a good contender as a solution.
Compare in Pairs will work best for minimum comparisons
# Initialization #
- if len(arr) is even, min = min(arr[0], arr[1]), max = max(arr[0], arr[1])
- if len(arr) is odd, min = min = arr[0], max = arr[0]
# Loop over pairs #
- Compare bigger of the element with the max, and smaller with min,
- if smaller element less than min, update min, similarly with max.
Total Number of comparisons -
For size = odd, 3(n - 1) / 2 where n is size of array
For size = even, 1 + 3*(n - 2)/2 = 3n/2 - 2
Below is the python code for the above pseudo-code
class Solution(object):
def min_max(self, arr):
size = len(arr)
if size == 1:
return arr[0], arr[0]
if size == 2:
return arr[0], arr[1]
min_n = None
max_n = None
index = None
if size % 2 == 0: # One comparison
min_n = min(arr[0], arr[1])
max_n = max(arr[0], arr[1])
st_index = 2
else:
min_n = arr[0]
max_n = arr[0]
st_index = 1
for index in range(st_index, size, 2):
if arr[index] < arr[index + 1]:
min_n = min(arr[index], min_n)
max_n = max(arr[index + 1], max_n)
else:
min_n = min(arr[index + 1], min_n)
max_n = max(arr[index], max_n)
return min_n, max_n
Just loop over the array once, keeping track of the max and min so far.