3

I am trying to create a list of integers and then scan it in order to find the minimum absolute value of the substractions of the elements of the list. I have created the list, but there is problem in the code which finds the minimum absolute value, as the result it shows is not correct. I think it is probably in the possitions of the elements of the list during the loops. Can you help me find it?

For example, when I create a list Α = [2, 7, 5, 9, 3, 1, 2], the result of min should be 0, but it is 1.

Here is my code:

min=1000
for i in range (1, N-1):
    for j in range (i+1, N):
        if (abs (A [i-1] - A [j-1])<min):
            min = abs (A [i-1] - A [j-1])
print ("%d" %min)
Jongware
  • 22,200
  • 8
  • 54
  • 100
Mike Johnson
  • 45
  • 1
  • 6

5 Answers5

5

You can do it like this:

A = [2, 7, 5, 9, 3, 1, 2]

temp = sorted(A)
min_diff = min([abs(i - j) for i, j in zip(temp [:-1], temp [1:])])

print(min_diff)  # -> 0

Sorting makes sure that the element pair (i, j) which produce the overall smallest difference would be a pair of consecutive elements. That makes the number of checks you have to perform much less than the brute force approach of all possible combinations.


Something a bit more clever that short-circuits:

A = [2, 7, 5, 9, 3, 1, 2]


def find_min_diff(my_list):
    if len(set(my_list)) != len(my_list):  # See note 1
        return 0
    else:
        temp = sorted(my_list)
        my_min = float('inf')
        for i, j in zip(temp [:-1], temp [1:]):
            diff = abs(i - j)
            if diff < my_min:
                my_min = diff
        return my_min

print(find_min_diff(A))  # -> 0

Notes:

1: Converting to set removes the duplicates so if the corresponding set has less elements than the original list it means that there is at least one duplicate value. But that necessarily means that the min absolute difference is 0 and we do not have to look any further.

I would be willing to bet that this is the fastest approach for all lists that would return 0.

Ma0
  • 15,057
  • 4
  • 35
  • 65
  • Sorting itself incurs a number of checks ;) – Andy G Feb 09 '18 at 10:54
  • @AndyG True but I would argue it is faster. – Ma0 Feb 09 '18 at 10:54
  • 3
    "As a result, you can traverse the list just once instead of twice." I find this a bit misleading, though, as this does not reduces the complexity from O(2n) to O(n), but from O(n²/2) to O(nlogn+n) – tobias_k Feb 09 '18 at 10:55
  • @tobias_k I was in the process of updating that part; you are totally right. – Ma0 Feb 09 '18 at 10:56
  • Sorting is good, numpy is better: Ev. Kounis: 131 µs, Espoir: 75 µs, jp_data: 71 ms, quamrana: 225 ms (A an array of 1000 random integers) - Oh, just noticed, that the numpy solution now also uses pre-sorting. So sorting was the major advance here. – Mr. T Feb 09 '18 at 11:40
  • 1
    It is. The second version clocks in at 26 µs (the same array as before) – Mr. T Feb 09 '18 at 11:56
  • Oh, wait. This is not a fair comparison, since you shortcut 0 and an array with 1000 random integers between zero and 100000 rarely has only unique elements. – Mr. T Feb 09 '18 at 12:01
  • 1
    @Piinthesky And why is that not fair? Short-circuiting is not a privileged I enjoy alone. – Ma0 Feb 09 '18 at 12:02
  • 1
    Let's summarise: Your solution is better on average and for shorter lists (probably numpy overhead). Numpy is better for longer lists and no repetitions. – Mr. T Feb 09 '18 at 12:08
4

You should not be subtracting 1 from j in the inner loop as you end up skipping the comparison of the last 2. It is better to make the adjustments in the loop ranges, rather than subtracting 1 (or not) in the loop code:

A = [2, 7, 5, 9, 3, 1, 2]

N = 7

mint = 1000

for i in range (0, N-1):
    for j in range (i+1, N):
        if (abs(A[i] - A[j]) < mint):
            mint = abs(A[i] - A[j])
            print(i, j)
            print(mint)
print(mint) # 0

I have also avoided the use of a built-in function name min.


To avoid the arbitrary, magic, number 1000, you can perform an initial check against None:

A = [2, 7, 5, 9, 3, 1, 2]

N = 7

mint = None

for i in range (0, N-1):
    for j in range (i+1, N):
        if mint is None:
            mint = abs(A[i] - A[j])
        elif (abs(A[i] - A[j]) < mint):
            mint = abs(A[i] - A[j])
            print(i, j)
            print(mint)
print(mint) # 0
Andy G
  • 19,232
  • 5
  • 47
  • 69
3

This is a brute-force solution:

from itertools import combinations

A = [2, 7, 5, 9, 3, 1, 2]

min(abs(i-j) for i, j in combinations(A, 2))  # 0
jpp
  • 159,742
  • 34
  • 281
  • 339
3

using numpy

import numpy as np
A = [2, 7, 5, 9, 3, 1, 2]
v = np.abs(np.diff(np.sort(np.array(A))))
np.min(v)

out : 0

Or You can use numpy only for the diff part like this :

v = min(abs(np.diff(sorted(A))))
Espoir Murhabazi
  • 5,973
  • 5
  • 42
  • 73
1

This is what you are looking for:

A = [2, 7, 5, 9, 3, 1, 2]

diffs = []
for index1, i in enumerate(A):
     for index2, j in enumerate(A):
         if index1 != index2:
             diffs.append(abs(i-j))             

print(min(diffs))

Output:

0

Updated to exclude subtraction of same items

quamrana
  • 37,849
  • 12
  • 53
  • 71