Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python中查找转换的算法_Python_Algorithm - Fatal编程技术网

Python中查找转换的算法

Python中查找转换的算法,python,algorithm,Python,Algorithm,我想实现一个算法,获取字母变化的索引。 我有下面的列表,在这里我想找到每个字母的开头变化,并把结果列表除了第一个。因为,对于第一个,我们应该得到它发生的最后一个索引。让我举个例子: letters=['A','A','A','A','A','A','A','A','A','A','A','A','B','C','C','X','D','X','B','B','A','A','A','A'] 过渡: 'A','A','A','A','A','A','A','A','A','A','A','A'

我想实现一个算法,获取字母变化的索引。 我有下面的列表,在这里我想找到每个字母的开头变化,并把结果列表除了第一个。因为,对于第一个,我们应该得到它发生的最后一个索引。让我举个例子:

letters=['A','A','A','A','A','A','A','A','A','A','A','A','B','C','C','X','D','X','B','B','A','A','A','A']
过渡:

 'A','A','A','A','A','A','A','A','A','A','A','A'-->'B'-->'C','C'-->'X'-->'D'-->'X'-->'B','B'-->'A','A','A','A'
在这里,字母A结束后,B开始,我们应该把最后一个字母A的索引和第一个字母B的索引等等,但是我们不应该把X字母包括在结果列表中。
预期结果:

  [(11, 'A'), (12, 'B'), (13, 'C'), (16, 'D'), (18, 'B'), (20, 'A')]
到目前为止,我已经完成了这段代码,这将查找除(11,'A')之外的其他项。如何修改代码以获得所需的结果

for i in range(len(letters)):
    if letters[i]!='X' and letters[i]!=letters[i-1]:
        result.append((i,(letters[i])))
我的结果是:

[(12, 'B'), (13, 'C'), (16, 'D'), (18, 'B'), (20, 'A')] ---> missing (11, 'A').
结果:(不是OP想要的结果,抱歉我一定是误解了。请看JSutton的ans)

这实际上是一封信在更改或列表结束之前的最后一个实例的索引。

你想要(或者,你不想要,正如你最后解释的-请参阅我的其他答案):

这将为您提供每个字母运行的最后一个索引,而不是Xs。如果需要相关字母后的第一个索引,请将-1切换为0

scanl
是一个reduce,返回中间结果

一般来说,先过滤还是后过滤都是有意义的,除非由于某种原因过滤成本很高,或者过滤可以在不增加复杂性的情况下轻松完成

此外,您的代码相对较难阅读和理解,因为您是按索引进行迭代的。这在python中是不寻常的,除非对索引进行数字操作。如果您访问每个项目,通常直接迭代


还有,你为什么想要这种特殊的格式?通常将格式设置为
(唯一项、数据)
,因为这可以很容易地放在
目录中

您的问题有点令人困惑,但此代码应该满足您的要求

firstChangeFound = False
for i in range(len(letters)):
    if letters[i]!='X' and letters[i]!=letters[i-1]:
        if not firstChangeFound:
            result.append((i-1, letters[i-1])) #Grab the last occurrence of the first character
            result.append((i, letters[i]))
            firstChangeFound = True
        else:
             result.append((i, letters[i])) 

借助字典使运行时间在输入数量上保持线性,下面是一个解决方案:

letters=['A','A','A','A','A','A','A','A','A','A','A','A','B','C','C','X','D','X','B','B','A','A','A','A']

def f(letters):
    result = []
    added = {}
    for i in range(len(letters)):
        if (i+1 == len(letters)):            
            break            
        if letters[i+1]!='X' and letters[i+1]!=letters[i]:
            if(i not in added and letters[i]!='X'):
                result.append((i, letters[i]))
                added[i] = letters[i]
            if(i+1 not in added):
                result.append((i+1, letters[i+1]))
                added[i+1] = letters[i+1]
    return result

基本上,我的解决方案总是尝试在发生更改的地方添加两个索引。但是字典(它有固定的时间查找功能)告诉我们是否已经添加了元素或者没有排除重复项。这将负责添加第一个元素。否则,您可以使用if语句来指示只运行一次的第一轮。然而,我认为这个解决方案具有相同的运行时间。只要不检查是否通过查找列表本身添加了元素(因为这在最坏的情况下是线性时间查找),这将导致O(n^2)时间,这是不好的

既然您已经解释过,您希望每个字母的第一个索引都位于第一个之后,下面是一行:

letters=['A','A','A','A','A','A','A','A','A','A','A','A','B','C','C','X','D','X','B','B','A','A','A','A']
[(n+1, b) for (n, (a,b)) in enumerate(zip(letters,letters[1:])) if a!=b and b!='X']
#=> [(12, 'B'), (13, 'C'), (16, 'D'), (18, 'B'), (20, 'A')]
现在,您的第一个条目不同了。为此,您需要使用一个配方来查找每个项目的最后一个索引:

import itertools
grouped = [(len(list(g))-1,k) for k,g in (itertools.groupby(letters))]
weird_transitions = [grouped[0]] + [(n+1, b) for (n, (a,b)) in enumerate(zip(letters,letters[1:])) if a!=b and b!='X']
#=> [(11, 'A'), (12, 'B'), (13, 'C'), (16, 'D'), (18, 'B'), (20, 'A')]
当然,您可以避免创建整个
grouped
列表,因为您只使用groupby中的第一项。我把它留给读者作为练习


如果X是第一个(一组)项目,这也会给您一个X作为第一个项目。因为你没有说你在做什么,或者为什么会有Xs,但是忽略了,我不知道这是否是正确的行为。如果不是,那么可能使用我的整个其他配方(在我的其他答案中),然后从中选择第一项

只需对代码进行最小的更改,并遵循Josh Caswell的建议:

for i, letter in enumerate(letters[1:], 1):
    if letter != 'X' and letters[i] != letters[i-1]:
        result.append((i, letter))
first_change = result[0][0]
first_stretch = ''.join(letters[:first_change]).rstrip('X')
if first_stretch:
    result.insert(0, (len(first_stretch) - 1, first_stretch[-1]))

这里有一个解决方案,它使用
groupby
生成一个序列,从中可以提取第一个和最后一个索引

import itertools
import functools
letters = ['A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'B', 'C', 'C', 'X', 'D', 'X', 'B', 'B', 'A', 'A', 'A', 'A']

groupbysecond = functools.partial(itertools.groupby,key=operator.itemgetter(1))

def transitions(letters):
    #segregate transition and non-transition indices
    grouped = groupbysecond(enumerate(zip(letters,letters[1:])))
    # extract first such entry from each group
    firsts = (next(l) for k,l in grouped)
    # group those entries together - where multiple, there are first and last
    # indices of the run of letters
    regrouped = groupbysecond((n,a) for n,(a,b) in firsts)
    # special case for first entry, which wants last index of first letter
    kfirst,lfirst = next(regrouped)
    firstitem = (tuple(lfirst)[-1],) if kfirst != 'X' else ()
    #return first item, and first index for all other letters
    return itertools.chain(firstitem,(next(l) for k,l in regrouped if k != 'X'))

这是我的建议。它有三个步骤

  • 首先,找到每个字母序列的所有起始索引
  • 将第一次非X运行中的索引替换为其运行结束时的索引,该索引将比下一次运行开始时的索引小一个
  • 过滤掉所有的X运行
  • 守则:

    def letter_runs(letters):
        prev = None
        results = []
    
        for index, letter in enumerate(letters):
            if letter != prev:
                prev = letter
                results.append((index, letter))
    
        if results[0][1] != "X":
            results[0] = (results[1][0]-1, results[0][1])
        else: # if first run is "X" second must be something else!
            results[1] = (results[2][0]-1, results[1][1])
    
        return [(index, letter) for index, letter in results if letter != "X"]
    

    这不是最好的修复方法,但您可以在循环之前的列表开头添加一个“X”。这可以解决问题。是的,当你在第一个i-1上时,你不能访问i-1。所以它不会附加在那里。相反,尝试做i+1==i和range(len(字母)-1)运算,我认为您期望的结果并不一致。最后的B是20而不是18。18是带有@sihrc的Xagree。我正在努力理解期望的结果是什么。为什么(16,'D')在结果中,而不是(14,'C')?此外,OP应该列出一个更清晰易读的列表(例如,为什么重复“a”两次以上?。@CoKoder如果你不想要Xs,为什么不在一开始就过滤掉它们?这不是我想做的。它统计每个字母出现的次数。但是,我想得到字母变化的索引。@CoKoder您现在可以看看这个。通过添加一行,它将为您提供累积值(即索引)。对不起,我不想导入我还没有的模块,因此这是我想到的第一个解决方案。首先,我想看看您的计时代码;其次,编写易于理解的代码比2倍的加速要重要得多,除非你正在进行大量的处理。很抱歉,不使用现有库作为编写更多代码的理由是一个糟糕的想法。我也发现我的代码更易于理解,虽然我知道这是基于个人偏好和知识。这符合OP的期望结果+字母=['A'、'A'、'A'、'A'、'A'、'A'、'A'、'A'、'A'、'A'、'A'、'X'、'C'、'X'、'D'、'X'、'B'、'B'、'A'、'A'、'A'、'A']。它打印[(12,'X'),(13,'C'),(16,'D'),(18,'B'),(20,'A')],这是错误的,因为(12,'X')应该是(11,'A')。@CoKoder如果你不得不对每一个答案抱怨,那么问题就是你的问题。事实上,这与您最初要求的输出相匹配。@Marcin我不是在抱怨它。我想说的是,答案应该满足其他情况。它不应该只针对某个特定的列表运行,对吗?您还没有提交任何其他案例,@CoKoder。您只给出了一个案例,这个答案满足。这个打印:[(11,'A'),
    for i, letter in enumerate(letters[1:], 1):
        if letter != 'X' and letters[i] != letters[i-1]:
            result.append((i, letter))
    first_change = result[0][0]
    first_stretch = ''.join(letters[:first_change]).rstrip('X')
    if first_stretch:
        result.insert(0, (len(first_stretch) - 1, first_stretch[-1]))
    
    import itertools
    import functools
    letters = ['A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'B', 'C', 'C', 'X', 'D', 'X', 'B', 'B', 'A', 'A', 'A', 'A']
    
    groupbysecond = functools.partial(itertools.groupby,key=operator.itemgetter(1))
    
    def transitions(letters):
        #segregate transition and non-transition indices
        grouped = groupbysecond(enumerate(zip(letters,letters[1:])))
        # extract first such entry from each group
        firsts = (next(l) for k,l in grouped)
        # group those entries together - where multiple, there are first and last
        # indices of the run of letters
        regrouped = groupbysecond((n,a) for n,(a,b) in firsts)
        # special case for first entry, which wants last index of first letter
        kfirst,lfirst = next(regrouped)
        firstitem = (tuple(lfirst)[-1],) if kfirst != 'X' else ()
        #return first item, and first index for all other letters
        return itertools.chain(firstitem,(next(l) for k,l in regrouped if k != 'X'))
    
    def letter_runs(letters):
        prev = None
        results = []
    
        for index, letter in enumerate(letters):
            if letter != prev:
                prev = letter
                results.append((index, letter))
    
        if results[0][1] != "X":
            results[0] = (results[1][0]-1, results[0][1])
        else: # if first run is "X" second must be something else!
            results[1] = (results[2][0]-1, results[1][1])
    
        return [(index, letter) for index, letter in results if letter != "X"]