Find elements in an array computing a given function value

5

Problem Description

Given:

  • a function \$f(a, b, c, d, e) = \frac{a \times b}c + \frac de\$
  • an array \$x = [x_1, x_2, x_3, x_4, ..., x_n]\$ of non-distinct integers
  • a target value \$k\$

What is the most efficient way, in terms of worst-case \$n\$, to find 5 distinct indices \$x_a, x_b, x_c, x_d, x_e\$ from \$x\$ such that \$f(x[x_a], x[x_b], x[x_c], x[x_d], x[x_e]) = k\$?

Example solutions

Example 1 (single solution):

\$x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], k = 5\$

Solution 1:

\$[1, 4, 2, 9, 3]\$ as \$f(x[1], x[4], x[2], x[9], x[3])\$ evaluates to \$5\$.

Example 2 (multiple solutions):

\$x = [0, -1, 1, -1, 0, -1, 0, 1], k = 0\$

Solution 2 (of many solutions):

\$[1, 2, 3, 5, 7]\$ OR \$[0, 1, 2, 4, 7]\$

Example 3 (no solution):

\$x = [0, -1, 1, -1, 0, -1, 0, 1], k = 8\$

Solution 3:

\$-1\$ (no solution)

Example 4 (intermediary floats):

\$x = [2, 3, 5, 4, 5, 1], k = 2\$

Solution 4:

\$[2, 3, 5, 4, 5]\$

versatile parsley

Posted 2018-06-12T22:07:22.243

Reputation: 51

Are you given a function with some number of variables an array and a third value? Also, I think the most efficient way is just brute force permutations from the array. – fəˈnɛtɪk – 2018-06-12T22:09:30.610

@fəˈnɛtɪk brute force with early stopping for branches of values larger than k? Also, the interchangeability of a*b == b*a seems important? – Seanny123 – 2018-06-12T22:14:42.000

@fəˈnɛtɪk The function is fixed. I think there might be some way to optimize this solution like two sum, but I haven't been able to figure out how yet.

– versatile parsley – 2018-06-12T22:15:53.703

1What exactly does the array contain? Floats? Integers? Positive integers? – Arnauld – 2018-06-12T23:36:47.327

May the intermediate calculations lead to float results? For instance, is [2,3,5,4,5] a valid solution for k=2? – Arnauld – 2018-06-13T11:26:11.030

@user202729 in terms of n – versatile parsley – 2018-06-13T21:53:54.913

@Arnauld yes, that's valid – versatile parsley – 2018-06-13T21:54:17.807

@user202729 I have added all the edits and clarifications requested in the discussion. Is this eligible to be reopened? – versatile parsley – 2018-06-20T01:47:13.487

@fəˈnɛtɪk That will still be O(n^5) in worst case n. – user202729 – 2018-06-21T02:30:11.143

2Do we know if k is always an integer? it is not specified in the question. – fəˈnɛtɪk – 2018-06-21T16:17:33.963

I guess that "non-distinct" means "not necessarily distinct"? It seems to be possible in O(n^4), maybe O(n^3 log n). – Christian Sievers – 2018-06-22T18:08:02.000

The first example also has the solution [0,2,3,5,1]. The last example gives the values instead of the indices. – Christian Sievers – 2018-06-23T13:52:20.440

Answers

1

Python3, O(n^3 log n)

from math import gcd
from functools import reduce

def lcm(x,y):
    return x//gcd(x,y)*y

def label(l):
    res=[(v,i) for i,v in enumerate(l)]
    res.sort()
    return res

def sortcombine(l):
    l.sort()
    res=[]
    oldval=None
    for val,how in l:
        if val==oldval:
            res[-1][1].append(how)
        else:
            oldval=val
            res.append((val,[how]))
    return res

def getabc(L,xl):
    N=len(xl)
    res=[]
    lasta=None
    for ai in range(N):
        a,al=xl[ai]
        if a==lasta:
            continue
        lasta=a
        lastb=None
        for bi in range(ai+1,N):
            b,bl=xl[bi]
            if b==lastb:
                continue
            lastb=b
            lastc=None
            for c,cl in xl:
                if c in [0,lastc] or cl in [al,bl]:
                    continue
                lastc=c
                res.append((L//c*a*b,(al,bl,cl)))
    return sortcombine(res)

def getde(L,xl):
    res=[]
    xl.reverse()
    lastd=None
    for d,dl in xl:
        if lastd==d:
            continue
        lastd=d
        laste=None
        for e,el in xl:
            if e in [0,laste] or el==dl:
                continue
            laste=e
            res.append((L//e*d,(dl,el)))
    return sortcombine(res)

def solve(l,k):
    xl=label(l)
    L=reduce(lcm,[v for v in l if v!=0],1)
    k*=L
    abc=getabc(L,xl)
    de=getde(L,xl)
    i=0
    j=len(de)-1
    while i<len(abc) and j>=0:
        v1,ls1=abc[i]
        v2,ls2=de[j]
        s=v1+v2
        if s==k:
            for l1 in ls1:
                s=set(l1)
                for l2 in ls2:
                    if s.isdisjoint(l2):
                        return list(l1+l2)
            i+=1
            j-=1
        elif s<k:
            i+=1
        else:
            j-=1
    return None

Try it online!

Multiply the equation by the LCM of the x values to avoid floating point or fractions. Find all possible values of ab/c (using ab=ba but that's only for a factor of 1/2) and d/e, sort them and try to add them to the wanted result. The sorting means we don't have to try all combinations.

There are O(n^3) possible values for ab/c, sorting them gives the log. The other steps are faster than that.

The while loop may be repeated O(n^3) times, but the case s==k can only occur O(n^2) times. If we don't find a solution and stop in this case, then the sizes of ls1 and ls2 (number of ways to obtain the corresponding value) must be in O(n) resp. O(1) (except in the case when the value is 0, but luckily that doesn't happen O(n) times).

Christian Sievers

Posted 2018-06-12T22:07:22.243

Reputation: 6 366

(but I have no proof that this is the most efficient way) – Christian Sievers – 2018-06-23T14:43:58.273

How does this determine that it isn't using the same element twice between AB/C and D/E – fəˈnɛtɪk – 2018-06-24T14:42:40.020

@fəˈnɛtɪk That's in the isdisjoint call. And there are a lot of yet unmentioned details how it handles repeated elements: group and label the elements, i.e. replace [1,2,1,...] with [(1,0),(1,1),(2,0),...], and use the low labels for abc and (by reversing the list) the high labels for de, so if there is intersection then that combination is really not possible. – Christian Sievers – 2018-06-24T20:02:20.700

(now changed the labels to be the indices, so in the above example we get [(1,0),(1,2),(2,1),...]) – Christian Sievers – 2018-06-29T23:02:56.493