Get dynamic submatrix and apply constraints - constraint-programming

I am really new to Constraint Programming and I am trying to solve a problem where from a two dimensional array, consisting of numbers, I need to take the least amount of sub arrays (2D) as possible, covering as much of the original 2D array as possible, obeying the following rules:
Every sub array must be a rectangle part of the original
The sum of numbers in each sub array must not exceed a specific number
Every sub array must have at least 2 numbers in it
For example for the following matrix:
3 5 1 4
5 1 2 8
0 8 1 3
8 3 2 1
For a maximum sum of 10, a solution would be:
3 -not picked
{ 5 1 4 }
{ 5 1 }
{ 2 8 }
{ 0 8 }
{ 1 3
2 1 }
8 -not picked
Right now I am using the diffn() equivalent of or-tools (MakeNonOverlappingBoxesConstraint()) to create the rectangles that are gonna cover the original array.
My problem is how to get the rectangles created by diffn() and split the original matrix based on the position and size of each one, so I can apply the Sum constraint.
If there is another way of achieving the same constraints without using the diffn() then I would try it out, but I can't think any other way.
Thank you!

The way to get a value from an array based on an IntVar, inside the solver, is by using the MakeElement() function and in this case the 2d version of it.
That way you can get a specific value from the matrix but not a range based on two IntVars (for example x - dx of rectangles). To accomplish the range part you can use a loop and a ConditionalExpression() to figure out if the specified value is in range.
For example in a 1d array, in order to get elements from data, positions x to x + dx would be as follows
int[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
IntVar x = solver.MakeIntVar(0, data.Length - 1);
IntVar dx = solver.MakeIntVar(1, data.Length);
solver.Add(x + dx <= data.Length);
IntVarVector range = new IntVarVector();
for (int i = 0; i < dx.Max(); i++)
{
range.Add(solver.MakeConditionalExpression((x + i < x + dx).Var() , solver.MakeElement(data, (x + i).Var()), 0).Var());
}
solver.Add(range.ToArray().Sum() <= 10);
In Case of the 2d array (as in the question) then you just iterate through both dimensions. The only difference is that the 2d version of MakeElement() accepts an IndexEvaluator2 item (LongLongToLong in C#) so you have to make your own class that inherits LongLongToLong and override the Run() function.
class DataValues: LongLongToLong
{
private int[,] _data;
private int _rows;
private int _cols;
public DataValues(int[,] data, int rows, int cols)
{
_rows = rows;
_cols = cols;
_data = data;
}
public override long Run(long arg0, long arg1)
{
if (arg0 >= _rows || arg1 >= _cols)
return 0;
return _data[arg0, arg1];
}
}
The only problem with this class is that it can ask for a value off the array, so we must handle it ourselves with if (arg0 >= _rows || arg1 >= _cols).
P.S. I dont know if this is the best method of accomplishing it, but it was the best I could think of, since I couldn't find anything similar online.

Related

An internal unit of company XYZ provides services to other departments

Example 1:
``
Given S="300.01" and B-["300.00", "200.00*,*100.00"].
R[0]="150.00" (=300.01 300.00/600.00) R[1]="100.00" (=150.01* 200.00/300.00)
R[2]="50.01" (=50.01*100.00/100.00)
Example 2 (Pay careful attention to this one).
Given S="1.00" and B=["0.05","1.00"]. 1. First we consider 1.00 because it is the largest,
a. 1.00*1.00/1.05~0.95238...
b. Round 0.95238... to "0.95". Rounding down to carry pennies to smaller departments. c. Set R[1]=0.95. Notice, this is in the same place as 1.00. It is the 2nd value in the result! 2. Now we have 0.05 left
Next we look at the smaller B[0]=0.05 department
a. 0.05 0.05/0.05 = 0.05 b. No rounding required
c. Set R[0]=0.05. R=["0.05", "0.95"]
`
Write a function:
class Solution { public String[] solution(String 5, String[] B); }
that, given a string S representing the total excess billables and an array B consisting of K strings representing the undiscounted bills for each customer. The return value should be an array of strings R (length M) in the same order as B representing the amount of the discount to each customer.
Notes:
The total S should be completely refunded. Neither more nor less than S should be
returned. Don't lose or gain a penny!
Be careful with the types you choose to represent currencies. Floating points numbers are notoriously error prone for precise calculations with currencies.
Test Output
Amounts should be rounded down to the nearest $0.01. By design, fractional pennies are pushed to groups with smaller unadjusted invoices.
Results should be represented with 2 decimal places of precision as strings, even if these are both zeroes. ex. "100.00" 5. You may assume sensible inputs. The total to be discounted will never exceed the total of the
unadjusted invoices.
Please do pay attention to returning the discounts in the same order as the incoming invoices.
Answer:::
def solution(A):
answer = 0
current_sum = 0
#Currently there is one empty subarray with sum 0
prefixSumCount = {0:1}
for list_element in A:
current_sum = current_sum + list_element
if current_sum in prefixSumCount:
answer = answer + prefixSumCount[current_sum]
if current_sum not in prefixSumCount:
prefixSumCount[current_sum] = 1
else:
prefixSumCount[current_sum] = prefixSumCount[current_sum] + 1
if answer > 1000000000:
return -1
else:
return answer
#Sample run
A = [2,-2,3,0,4,-7]
print(solution(A))
strong text
Find my solution for JavaScript
You can avoid function sumArray and use the sum funciton with reducer within solution function.
enter code here
function solution(S, B) {
// write your code in JavaScript (Node.js 8.9.4)
let copyArray=[...B];
let solutionObj={};
//ordered array to consider last first
copyArray.sort();
//calculating sum of values within array
let sumArray=B.reduce((acc,value)=> acc+Number(value),0);
//calculating values of array
//loop for ading on to solvin array
let initial=S;
for(i=copyArray.length-1;i>=0;i--){
//obtaining index of value addded to solution array
let index=B.indexOf(copyArray[i]);
let value=initial*copyArray[i]/sumArray;
value=i==0?Math.ceil(value*100)/100:Math.floor(value*100)/100;
solutionObj[index]=value.toFixed(2);
}
return Object.values(solutionObj) ;
}
console.log(solution(300.01,["300.00","200.00","100.00"]))
console.log(solution(1.00,["0.05","1.00"]))
These are the resulting entries
Solution in java for the same coding exercise
public String[] solution(String S, String[] B) {
List<Double> list = Arrays.stream(B).sorted(Comparator.comparingDouble(v->Double.valueOf((String) v)).reversed()).map(Double::parseDouble).collect(Collectors.toList());
Double S1 = Double.valueOf(S);
String R[] = new String[B.length];
Double total = 0.00;
for (int i = 0; i< list.size(); i++){
Double individualValue = list.get(i);
Double sumTotal = 0.0;
for(int j = i+1;j < list.size(); j++){
sumTotal+=list.get(j);
}
BigDecimal data = new BigDecimal(S1 * (individualValue / (individualValue + sumTotal)));
data = data.setScale(2, RoundingMode.FLOOR);
total+=data.doubleValue();
R[i] = String.valueOf(data);
S1 = S1 - Double.valueOf(R[i]);
}
Double diff = new BigDecimal(Double.valueOf(S) - total).setScale(2, RoundingMode.HALF_DOWN).doubleValue();
if (diff > 0) {
R[B.length - 1] = String.valueOf(Double.valueOf(R[B.length - 1]) + diff);
}
return R;
}

What is the worst case for binary search

Where should an element be located in the array so that the run time of the Binary search algorithm is O(log n)?
The first or last element will give the worst case complexity in binary search as you'll have to do maximum no of comparisons.
Example:
1 2 3 4 5 6 7 8 9
Here searching for 1 will give you the worst case, with the result coming in 4th pass.
1 2 3 4 5 6 7 8
In this case, searching for 8 will give the worst case, with the result coming in 4 passes.
Note that in the second case searching for 1 (the first element) can be done in just 3 passes. (compare 1 & 4, compare 1 & 2 and finally 1)
So, if no. of elements are even, the last element gives the worst case.
This is assuming all arrays are 0 indexed. This happens due to considering the mid as float of (start + end) /2.
// Java implementation of iterative Binary Search
class BinarySearch
{
// Returns index of x if it is present in arr[],
// else return -1
int binarySearch(int arr[], int x)
{
int l = 0, r = arr.length - 1;
while (l <= r)
{
int m = l + (r-l)/2;
// Check if x is present at mid
if (arr[m] == x)
return m;
// If x greater, ignore left half
if (arr[m] < x)
l = m + 1;
// If x is smaller, ignore right half
else
r = m - 1;
}
// if we reach here, then element was
// not present
return -1;
}
// Driver method to test above
public static void main(String args[])
{
BinarySearch ob = new BinarySearch();
int arr[] = {2, 3, 4, 10, 40};
int n = arr.length;
int x = 10;
int result = ob.binarySearch(arr, x);
if (result == -1)
System.out.println("Element not present");
else
System.out.println("Element found at " +
"index " + result);
}
}
Time Complexity:
The time complexity of Binary Search can be written as
T(n) = T(n/2) + c
The above recurrence can be solved either using Recurrence T ree method or Master method. It falls in case II of Master Method and solution of the recurrence is Theta(Logn).
Auxiliary Space: O(1) in case of iterative implementation. In case of recursive implementation, O(Logn) recursion call stack space.

How do I return the smallest value using a for loop?

I am given a limit, and I have to return the smallest value for n to make it true: 1+2+3+4+...+n >= limit. I feel like there's one thing missing, but I can't tell.
public int whenToReachLimit(int limit) {
int sum = 0;
for (int i = 1; sum < limit; i++) {
sum = sum + i;
}
return sum;
}
The output would be:
1 : 1
4 : 3
10 : 4
You get avoid the loop to compute the sum of the n first integers, using:
Thus the inequality becomes:
Notice that the left-hand side is positive (if n is negative, the sum is empty) and strictly increasing. Notice also that you are looking for the first integer satisfying the inequality. The idea here is first to replace the inequality by an equality which will allow us to solve the equation for n. In a second step, the possibly non-integer solution will be rounder to the closest integer.
Solving this equation for n should give you two solutions. The negative one can be discarded (remember n is positive). That is:
Finally, let's round this solution to the closest integer that will also satisfy the inequality:
NB: it can be overkilled for small inputs
I'm not sure if I know exactly what you want to do. But I would recommend to make a "practice run".
If Limit = 0 the function returns 0
If Limit = 1 the function returns 1
If Limit = 2 the function return 3
If Limit = 3 the function return 3
If Limit = 4 the function return 6
If Limit = 5 the function return 6
Now you decide by your own if the functions does what you're expecting.
I've found the answer. Turns out it doesn't work with a for loop which I find odd. But this is the answer to my own question.
public int whenToReachLimit(int limit) {
int n = 0;
int sum = 0;
while (sum < limit) {
sum += n;
n++;
}
return n-1;
}
You don't want to return sum, you want to return n (smallest possible value satisfying the given requirement).
return i-1 instead of sum.

Maximum element in array which is equal to product of two elements in array

We need to find the maximum element in an array which is also equal to product of two elements in the same array. For example [2,3,6,8] , here 6=2*3 so answer is 6.
My approach was to sort the array and followed by a two pointer method which checked whether the product exist for each element. This is o(nlog(n)) + O(n^2) = O(n^2) approach. Is there a faster way to this ?
There is a slight better solution with O(n * sqrt(n)) if you are allowed to use O(M) memory M = max number in A[i]
Use an array of size M to mark every number while you traverse them from smaller to bigger number.
For each number try all its factors and see if those were already present in the array map.
Here is a pseudo code for that:
#define M 1000000
int array_map[M+2];
int ans = -1;
sort(A,A+n);
for(i=0;i<n;i++) {
for(j=1;j<=sqrt(A[i]);j++) {
int num1 = j;
if(A[i]%num1==0) {
int num2 = A[i]/num1;
if(array_map[num1] && array_map[num2]) {
if(num1==num2) {
if(array_map[num1]>=2) ans = A[i];
} else {
ans = A[i];
}
}
}
}
array_map[A[i]]++;
}
There is an ever better approach if you know how to find all possible factors in log(M) this just becomes O(n*logM). You have to use sieve and backtracking for that
#JerryGoyal 's solution is correct. However, I think it can be optimized even further if instead of using B pointer, we use binary search to find the other factor of product if arr[c] is divisible by arr[a]. Here's the modification for his code:
for(c=n-1;(c>1)&& (max==-1);c--){ // loop through C
for(a=0;(a<c-1)&&(max==-1);a++){ // loop through A
if(arr[c]%arr[a]==0) // If arr[c] is divisible by arr[a]
{
if(binary_search(a+1, c-1, (arr[c]/arr[a]))) //#include<algorithm>
{
max = arr[c]; // if the other factor x of arr[c] is also in the array such that arr[c] = arr[a] * x
break;
}
}
}
}
I would have commented this on his solution, unfortunately I lack the reputation to do so.
Try this.
Written in c++
#include <vector>
#include <algorithm>
using namespace std;
int MaxElement(vector< int > Input)
{
sort(Input.begin(), Input.end());
int LargestElementOfInput = 0;
int i = 0;
while (i < Input.size() - 1)
{
if (LargestElementOfInput == Input[Input.size() - (i + 1)])
{
i++;
continue;
}
else
{
if (Input[i] != 0)
{
LargestElementOfInput = Input[Input.size() - (i + 1)];
int AllowedValue = LargestElementOfInput / Input[i];
int j = 0;
while (j < Input.size())
{
if (Input[j] > AllowedValue)
break;
else if (j == i)
{
j++;
continue;
}
else
{
int Product = Input[i] * Input[j++];
if (Product == LargestElementOfInput)
return Product;
}
}
}
i++;
}
}
return -1;
}
Once you have sorted the array, then you can use it to your advantage as below.
One improvement I can see - since you want to find the max element that meets the criteria,
Start from the right most element of the array. (8)
Divide that with the first element of the array. (8/2 = 4).
Now continue with the double pointer approach, till the element at second pointer is less than the value from the step 2 above or the match is found. (i.e., till second pointer value is < 4 or match is found).
If the match is found, then you got the max element.
Else, continue the loop with next highest element from the array. (6).
Efficient solution:
2 3 8 6
Sort the array
keep 3 pointers C, B and A.
Keeping C at the last and A at 0 index and B at 1st index.
traverse the array using pointers A and B till C and check if A*B=C exists or not.
If it exists then C is your answer.
Else, Move C a position back and traverse again keeping A at 0 and B at 1st index.
Keep repeating this till you get the sum or C reaches at 1st index.
Here's the complete solution:
int arr[] = new int[]{2, 3, 8, 6};
Arrays.sort(arr);
int n=arr.length;
int a,b,c,prod,max=-1;
for(c=n-1;(c>1)&& (max==-1);c--){ // loop through C
for(a=0;(a<c-1)&&(max==-1);a++){ // loop through A
for(b=a+1;b<c;b++){ // loop through B
prod=arr[a]*arr[b];
if(prod==arr[c]){
System.out.println("A: "+arr[a]+" B: "+arr[b]);
max=arr[c];
break;
}
if(prod>arr[c]){ // no need to go further
break;
}
}
}
}
System.out.println(max);
I came up with below solution where i am using one array list, and following one formula:
divisor(a or b) X quotient(b or a) = dividend(c)
Sort the array.
Put array into Collection Col.(ex. which has faster lookup, and maintains insertion order)
Have 2 pointer a,c.
keep c at last, and a at 0.
try to follow (divisor(a or b) X quotient(b or a) = dividend(c)).
Check if a is divisor of c, if yes then check for b in col.(a
If a is divisor and list has b, then c is the answer.
else increase a by 1, follow step 5, 6 till c-1.
if max not found then decrease c index, and follow the steps 4 and 5.
Check this C# solution:
-Loop through each element,
-loop and multiply each element with other elements,
-verify if the product exists in the array and is the max
private static int GetGreatest(int[] input)
{
int max = 0;
int p = 0; //product of pairs
//loop through the input array
for (int i = 0; i < input.Length; i++)
{
for (int j = i + 1; j < input.Length; j++)
{
p = input[i] * input[j];
if (p > max && Array.IndexOf(input, p) != -1)
{
max = p;
}
}
}
return max;
}
Time complexity O(n^2)

How to count cell numbers based on a set of numbers

I am looking for a function which counts how many numbers in a range of cells are in the set of numbers
For example I have the set of numbers(1,2,3) and my cells contains 1 | 2 | 3 | 4 | 5 | 3 , the count should return 4
I have tried using countif but no success, I would like to have an excel function Ex.: =countif(A1:D5,...)
How about this? Assume data is in range A1:D5 and you want to count cells with a value of 1, 2 or 3:
=SUM(COUNTIF(A1:D5, {"1","2","3"}))
I hope my pseudo-code would be understandable
int count(int *set, int set_size, int *cells, int cells_size)
{
int v = 0;
// For every number in set
for(int i = 0; i < set_size; ++i)
{
// Loop through every number in cells
for(int j = 0; j < cells_size; ++j)
{
// If number in cells equals number in set, increment v
if(cells[j] == set[i])
{
v++;
}
}
}
// Result is in v, return it
return v;
}
Of course you can optimize a bit with using better containers than just arrays and sizes of them, but I hope you get the basics from this.
Note I used C-like language for pseudo-code, if anything is unclear I can explain further.

Resources