Python 整数列表到范围

Python 整数列表到范围,python,algorithm,python-2.7,Python,Algorithm,Python 2.7,我需要将整数列表转换为包含列表中所有范围的字符串。 例如,输出应如下所示: getIntRangesFromList([1,3,7,2,11,8,9,11,12,15]) -> "1-3,7-9,11-12,15" 因此,输入未排序,可能存在重复值。列表的大小从一个元素到4k元素不等。最小值和最大值分别为1和4094 这是性能关键代码的一部分。我一直在尝试优化它,但我找不到一种方法来加快它。这是我当前的代码: def _getIntRangesFromList(list): if

我需要将整数列表转换为包含列表中所有范围的字符串。 例如,输出应如下所示:

getIntRangesFromList([1,3,7,2,11,8,9,11,12,15]) -> "1-3,7-9,11-12,15"
因此,输入未排序,可能存在重复值。列表的大小从一个元素到4k元素不等。最小值和最大值分别为1和4094

这是性能关键代码的一部分。我一直在尝试优化它,但我找不到一种方法来加快它。这是我当前的代码:

def _getIntRangesFromList(list):
    if (list==[]):
        return ''
    list.sort()
    ranges = [[list[0],list[0]]] # ranges contains the start and end values of each range found
    for val in list:
        r = ranges[-1]
        if val==r[1]+1:
            r[1] = val
        elif val>r[1]+1:
            ranges.append([val,val])
    return ",".join(["-".join([str(y) for y in x]) if x[0]!=x[1] else str(x[0]) for x in ranges])

关于如何更快地完成这项任务,您有什么想法吗?

这可能是本模块的任务

这将为您提供
1-3、7-9、11-12、15

要了解发生了什么,您可能需要检查
组的内容

import itertools
list_num = [1, 2, 3, 7, 8, 9, 11, 12, 15]

groups = (list(x) for _, x in
          itertools.groupby(list_num, lambda x, c=itertools.count(): x - next(c)))
for element in groups:
    print('element={}'.format(element))
这将为您提供以下输出

element=[1, 2, 3]
element=[7, 8, 9]
element=[11, 12]
element=[15]
基本思想是让计数器与数字平行运行
groupby
将为与计数器当前值具有相同数字距离的数字创建单独的组


我不知道这在您的Python版本上是否更快。你得自己检查一下。在我的设置中,使用此数据集速度较慢,但使用更多元素时速度较快。

这可能是模块的任务

def _to_range(l, start, stop, idx, result):
    if idx == len(l):
        result.append((start, stop))
        return result
    if l[idx] - stop > 1:
        result.append((start, stop))
        return _to_range(l, l[idx], l[idx], idx + 1, result)
    return _to_range(l, start, l[idx], idx + 1, result)

def get_range(l):
    if not l:
        return []
    return _to_range(l, start = l[0], stop = l[0], idx = 0, result = [])

l = [1, 2, 3, 7, 8, 9, 11, 12, 15]
result = get_range(l)
print(result) 
>>> [(1, 3), (7, 9), (11, 12), (15, 15)]
# I think it's better to fetch the data as it is and if needed, change it 
# with
print(','.join('-'.join([str(start), str(stop)]) for start, stop in result))
>>> 1-3,7-9,11-12,15-15
这将为您提供
1-3、7-9、11-12、15

要了解发生了什么,您可能需要检查
组的内容

import itertools
list_num = [1, 2, 3, 7, 8, 9, 11, 12, 15]

groups = (list(x) for _, x in
          itertools.groupby(list_num, lambda x, c=itertools.count(): x - next(c)))
for element in groups:
    print('element={}'.format(element))
这将为您提供以下输出

element=[1, 2, 3]
element=[7, 8, 9]
element=[11, 12]
element=[15]
基本思想是让计数器与数字平行运行
groupby
将为与计数器当前值具有相同数字距离的数字创建单独的组

我不知道这在您的Python版本上是否更快。你得自己检查一下。在我的设置中,使用此数据集速度较慢,但使用更多元素时速度更快

def _to_range(l, start, stop, idx, result):
    if idx == len(l):
        result.append((start, stop))
        return result
    if l[idx] - stop > 1:
        result.append((start, stop))
        return _to_range(l, l[idx], l[idx], idx + 1, result)
    return _to_range(l, start, l[idx], idx + 1, result)

def get_range(l):
    if not l:
        return []
    return _to_range(l, start = l[0], stop = l[0], idx = 0, result = [])

l = [1, 2, 3, 7, 8, 9, 11, 12, 15]
result = get_range(l)
print(result) 
>>> [(1, 3), (7, 9), (11, 12), (15, 15)]
# I think it's better to fetch the data as it is and if needed, change it 
# with
print(','.join('-'.join([str(start), str(stop)]) for start, stop in result))
>>> 1-3,7-9,11-12,15-15
除非您根本不关心数据,否则您可以在_to_range函数中附加str(start)+'-'+str(stop),这样以后就不需要键入额外的'-'.join方法


除非您根本不关心数据,否则您可以在_to _range函数中附加str(start)+'-'+str(stop),这样以后就不需要键入额外的'-'.join方法。

我将集中讨论性能这一主要问题。我将给出两种解决方案:

1) 如果存储的整数的边界在A和B之间,并且您可以使用(B-A+2)元素(例如A=0和B=1000000)创建布尔数组(甚至您可以选择一个位数组来扩展存储范围),我们可以这样做(我将用C#编写,抱歉XD)。这在O(A-B)中运行,如果A-B小于数字的数量,这是一个很好的解决方案:

public string getIntRangesFromList(int[] numbers)
    {
        //You can change this 2 constants
        const int A = 0;    
        const int B = 1000000;

        //Create an array with all its values in false by default
        //Last value always will be in false in propourse, as you can see it storage 1 value more than needed for 2nd cycle 
        bool[] apparitions = new bool[B - A + 2];
        int minNumber = B + 1;
        int maxNumber = A - 1;
        int pos;
        for (int i = 0; i < numbers.Length; i++)
        {
            pos = numbers[i] - A;
            apparitions[pos] = true;

            if (minNumber > pos)
            {
                minNumber = pos;
            }
            if (maxNumber < pos)
            {
                maxNumber = pos;
            }
        }

        //I will mantain the concatenation simple, but you can make it faster to improve performance
        string result = "";
        bool isInRange = false;
        bool isFirstRange = true;
        int firstPosOfRange = 0; //Irrelevant what is its initial value
        for (int i = minNumber; i <= maxNumber + 1; i++)
        {
            if (!isInRange)
            {
                if (apparitions[i])
                {
                    if (!isFirstRange)
                    {
                        result += ",";
                    }
                    else
                    {
                        isFirstRange = false;
                    }

                    result += (i + A);
                    isInRange = true;
                    firstPosOfRange = i;
                }
            }
            else
            {
                if (!apparitions[i])
                {
                    if (i > firstPosOfRange + 1)
                    {
                        result += "-" + (i + A - 1);
                    }
                    isInRange = false;
                }
            }
        }

        return result;
    }
公共字符串getIntRangesFromList(int[]数字)
{
//您可以更改这两个常量
常数INTA=0;
常数int B=1000000;
//创建一个数组,其所有值默认为false
//最后一个值在ProCourse中始终为false,因为您可以看到它存储的值比第二个周期所需的值多1
bool[]幻影=新bool[B-A+2];
int minNumber=B+1;
int maxNumber=A-1;
int pos;
for(int i=0;ipos)
{
minNumber=pos;
}
如果(最大数量<位置)
{
maxNumber=pos;
}
}
//我将保持连接的简单性,但您可以使其更快以提高性能
字符串结果=”;
bool-isInRange=false;
bool isFirstRange=true;
int firstPosOfRange=0;//它的初始值是多少
for(int i=minNumber;i firstPosOfRange+1)
{
结果+=“-”+(i+A-1);
}
isInRange=false;
}
}
}
返回结果;
}
2) O(N*logn)

公共字符串getIntRangesFromList2(int[]数字)
{
字符串结果=”;
如果(number.Length>0)
{
numbers.OrderBy(x=>x);//排序并使算法复杂度为O(N*logn)
结果+=数字[0];
int countNumbersInRange=1;
for(int i=1;i1)
{
结果+=“-”+数字[i-1];
}
结果+=”,“+数字[i];
countNumbersInRange=1;
}
其他的
{
countNumbersInRange++;
}
}
}
返回结果;
}

我将集中讨论性能,这是您的主要问题。我将给出两种解决方案:

1) 如果存储的整数的边界在A和B之间,并且您可以使用(B-A+2)元素(例如A=0和B=1000000)创建布尔数组(甚至您可以选择一个位数组来扩展存储范围),我们可以这样做(我将用C#编写,抱歉XD)。这在O(A-B)中运行,如果A-B小于数字的数量,这是一个很好的解决方案:

public string getIntRangesFromList(int[] numbers)
    {
        //You can change this 2 constants
        const int A = 0;    
        const int B = 1000000;

        //Create an array with all its values in false by default
        //Last value always will be in false in propourse, as you can see it storage 1 value more than needed for 2nd cycle 
        bool[] apparitions = new bool[B - A + 2];
        int minNumber = B + 1;
        int maxNumber = A - 1;
        int pos;
        for (int i = 0; i < numbers.Length; i++)
        {
            pos = numbers[i] - A;
            apparitions[pos] = true;

            if (minNumber > pos)
            {
                minNumber = pos;
            }
            if (maxNumber < pos)
            {
                maxNumber = pos;
            }
        }

        //I will mantain the concatenation simple, but you can make it faster to improve performance
        string result = "";
        bool isInRange = false;
        bool isFirstRange = true;
        int firstPosOfRange = 0; //Irrelevant what is its initial value
        for (int i = minNumber; i <= maxNumber + 1; i++)
        {
            if (!isInRange)
            {
                if (apparitions[i])
                {
                    if (!isFirstRange)
                    {
                        result += ",";
                    }
                    else
                    {
                        isFirstRange = false;
                    }

                    result += (i + A);
                    isInRange = true;
                    firstPosOfRange = i;
                }
            }
            else
            {
                if (!apparitions[i])
                {
                    if (i > firstPosOfRange + 1)
                    {
                        result += "-" + (i + A - 1);
                    }
                    isInRange = false;
                }
            }
        }

        return result;
    }
公共字符串getIntRangesFromList(int[]数字)
{
//您可以更改这两个常量
常数INTA=0;
常数int B=1000000;
//创建一个数组,其所有值默认为false
//最后一个值在ProCourse中始终为false,因为您可以看到它存储的值比第二个周期所需的值多1
bool[]幻影=新bool[B-A+2];
int minNumber=B+1;
int maxNumber=A-1;
int pos;
for(int i=0;i