给定一组数字,在其中找到最长算术级数的长度

时间:2019-10-05 06:29:13

标签: python algorithm

我的方法如下
1.我正在创建一个字典,用于存储所有数字对和计数之间的差异
2.键包含差异,值是一个列表。列表的第一个索引是差异出现的次数,随后的索引仅代表遵循算术级数的数字

我为此编写了以下代码

d = {}
for i in range(len(A)-1):
    for j in range(i+1, len(A)):
        if A[i]-A[j] in d.keys():
            d[A[i]-A[j]][0] += 1
            d[A[i]-A[j]].append(A[j])
        else:
            d[A[i]-A[j]] = [2, A[i], A[j]]
# Get the key,value pair having the max value
k,v  = max(d.items(), key=lambda k: k[1])
print(v[0])

例如,如果输入为[20,1,15,3,10,5,8],则我的输出为4

但是,我的代码对于以下输入[83,20,17,43,52,78,68,45]失败。
预期的结果是2,但是我得到3。当我打印字典的内容时,我发现字典中有类似

的条目
-25: [3, 20, 45, 68], -26: [3, 17, 43, 78], -35: [3, 17, 52, 78]

我不明白为什么会出现它们,因为在-25的情况下,差68和45不等于25,因此我在将值添加到字典之前进行了检查。 有人可以指出我代码中的错误吗?

我的完整输出是

{63: [2, 83, 20], 66: [2, 83, 17], 40: [2, 83, 43], 31: [2, 83, 52], 5: [2, 83, 78], 15: [2, 83, 68], 38: [2, 83, 45], 3: [2, 20, 17], -23: [2, 20, 43], -32: [2, 20, 52], -58: [2, 20, 78], -48: [2, 20, 68], -25: [3, 20, 45, 68], -26: [3, 17, 43, 78], -35: [3, 17, 52, 78], -61: [2, 17, 78], -51: [2, 17, 68], -28: [2, 17, 45], -9: [2, 43, 52], -2: [2, 43, 45], -16: [2, 52, 68], 7: [2, 52, 45], 10: [2, 78, 68], 33: [2, 78, 45], 23: [2, 68, 45]}

4 个答案:

答案 0 :(得分:1)

我认为您使用的算法不能解决您要解决的问题。 主要问题是扩展算术序列的标准未考虑序列本身。 考虑例如:

A = [10, 20, 50, 60]

存在两个属于差异-10的序列,因此dict实际上不是一个很好的数据结构,不能以您的算法为基础,至少不是您打算的方式。


编辑

您可以通过多种方式解决问题。 一种非常直接但不是非常有效的方法是:

  • 对所有元素进行排序(这不是严格必要的,但是会使其余元素更有效)
  • 首先考虑所有元素
  • 确定所有元素是否实际上都是算术级数
    • 仅计算连续元素的差
    • 如果它们都具有相同的值,则其算术级数
  • 如果它们是算术级数,则完成了,如果不是,则迭代删除元素并重复上述操作,直到找到算术级数为止。

在此代码中,如下所示:

import itertools


def is_arithmetic_progression(items):
    diffs = [x - y for x, y in zip(items[1:], items[:-1])]
    return diffs[1:] == diffs[:-1]


def skip_items(items, indexes):
    return [item for i, item in enumerate(items) if i not in indexes]


def lap_combs(items, sorting=True):
    if sorting:
        items = sorted(items)
    for i in range(len(items)):
        for indexes in itertools.combinations(range(len(items)), i):
            new_items = skip_items(items, indexes)
            if is_arithmetic_progression(new_items, False):
                return new_items


items = [83, 20, 17, 43, 52, 78, 68, 45]
longest_ap = lap_combs(items)
print(longest_ap)
# [78, 83]

items = [83, 20, 17, 43, 52, 78, 68, 45, 70]
longest_ap = lap_combs(items)
print(longest_ap)
# [20, 45, 70]

编辑2:

请注意,可以通过分析排序项目的差异来进一步优化:

  • 计算任意两个项目之间的所有差异
  • 对于给定的差异,计算项目中有多少个元素
  • 跟踪找到给定项目和元素的最大元素数量
  • 如果对于给定的差异,项目所包含的数字不能超过最大值,请跳过
  • 如果元素数超过项目数的一半,则停止

在代码中,它看起来像:

def seed_diff_len_to_seq(seed, diff, length):
    return [seed + diff * k for k in range(length)]


def lap_diffs(items):
    half_len_items = len(items) // 2
    span = max(items) - min(items)
    seed = 0
    max_seq_len = 0
    diff = None
    set_items = set(items)
    for item_i, item_j in itertools.combinations(sorted(items), 2):
        diff_ji = item_j - item_i
        if diff_ji == 0:
            seq_len = sum(1 for item in items if item == item_i)
        elif abs(diff_ji * max_seq_len) > span:
            continue
        else:
            seq_len = 2
            while item_i + diff_ji * seq_len in set_items:
                seq_len += 1
        if seq_len > max_seq_len:
            max_seq_len = seq_len
            seed = item_i
            diff = diff_ji
            if seq_len > half_len_items:
                break
    return seed_diff_len_to_seq(seed, diff, max_seq_len)

对此进行基准化(包括将@VPfB's solution标记为lap_maxprogr(),将@rusu_ro1's solution标记为lap_dict(),而lap_combs()至少慢1个数量级,并且不包含在图)显示lap_diffs()是最快的(输入的项目数超过大约一打之后):

bm_full bm_zoom

(全面分析here。)

(请注意,lap_diffs()使用与lap_maxprogr()基本上相同的方法,但有更多优化)。

答案 1 :(得分:0)

注意:

  1. 您的列表中有43个,而68-43 = 25
  2. 有多个具有相同差异的LPA

有2个错误:

  1. 您检查d.keys()中是否存在差异,但是不检查数字是否在LPA中。例如:当数字为43时,68 diff为25,但当前列表为[2,20,45]。在这种情况下,存在多个具有相同差异25的LAP。
  2. 您插入了A[j],但没有插入A[i],因此您只插入了68,而不是43

答案 2 :(得分:0)

您必须认为btw数之差不足以作为关键,例如8-3 = 5,也5-0 = 5

您可以尝试:

def length_longest_AP(A):
    d = {}
    A.sort()
    for index_i, i in enumerate(A[:-1]):
        for index, j in enumerate(A[index_i + 1 :]):
            dif = j - i
            key = f'{i}_{index_i}_{dif}'

            if key in d:
                continue


            d[key] = [i, j]
            possible_next = j + dif
            try:
                current_index = index_i + 1 + index
                possible_next_one_index = current_index + A[current_index + 1:].index(possible_next)

                # avoiding repetitions 
                if current_index == possible_next_one_index:
                    possible_next_one_index = current_index + 1

            except ValueError:
                continue

            while True:
                d[key].append(possible_next)
                possible_next += dif
                try:
                    current_index = possible_next_one_index
                    possible_next_one_index = current_index + A[current_index + 1:].index(possible_next)

                    # avoiding repetitions 
                    if current_index == possible_next_one_index:
                        possible_next_one_index = current_index + 1

                except ValueError:
                    break


    return len(max(d.values(), key=len))

print(length_longest_AP([20,1,15,3,10,5,8]))
print(length_longest_AP([1,1,1,1,1,1,1,1,1,1]))
print(length_longest_AP([83,20,17,43,52,78,68,45]))

输出:

4
10
2

答案 3 :(得分:0)

另一种方法。将每对视为进展的开始,并尝试扩展该进展。

重要的优化:如果进度至少包含项目总数的一半,则它必须是最长的进度。

import itertools

def maxprogr(items):
    # 2 or more numbers required to find any progression
    # if there are multiple maximums, the first one is returned 
    half = len(items) / 2
    pmax = (None, None, 0)
    for start, stop in itertools.combinations(sorted(items), 2):
        diff = stop - start
        if diff == 0:
            plen = sum(1 for x in items if x == start) 
        else:        
            plen = 2 
            while start + diff*plen in items:
                plen += 1
        if plen > pmax[2]:
            pmax = (start, diff, plen)
        if plen > half:
            break
    return pmax

# and some tests:
def print_maxprogr(items):
    print("MAX: start={0[0]}, diff=+{0[1]}, len={0[2]}".format(maxprogr(items)))

test1 = [20,1,15,3,10,5,8]
print_maxprogr(test1)
test2 = [83, 20, 17, 43, 52, 78, 68, 45, 70] 
print_maxprogr(test2)
test3 = [83, 20, 17, 43, 52, 78, 68, 45]
print_maxprogr(test3)
test4 = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
print_maxprogr(test4)

测试输出:

MAX: start=5, diff=+5, len=4
MAX: start=20, diff=+25, len=3
MAX: start=17, diff=+3, len=2
MAX: start=1, diff=+0, len=10

(发布后1小时更新了代码,以解决小错误)