Вы можете изменить свое мышление - попытаться потерпеть неудачу как можно быстрее, когда строка определенно не имеет даты:
null
length
не равно 8 (на основе вашего примера формата даты!) Если ничего из этого не применимо, попробуйте проанализировать его - предпочтительно с предварительно созданным статическим Format
объектом, не создавайте его при каждом запуске метода.
РЕДАКТИРОВАТЬ после комментариев
Основываясь на этой изящной уловке , я написал метод быстрой проверки. Это выглядит некрасиво, но значительно быстрее, чем обычные библиотечные методы (которые следует использовать в любой стандартной ситуации!), Потому что опирается на ваш конкретный формат даты и не создает объект Date
. Это обрабатывает дату как int
и продолжается от этого.
Я чуть-чуть протестировал метод daysInMonth()
( условие високосного года, взятое у Питера Лоури ), поэтому надеюсь, что явной ошибки нет.
Быстрый (оценочный!) Микробенчмарк показал ускорение в 30 раз.
public static boolean isValidDate(String dateString) {
if (dateString == null || dateString.length() != "yyyyMMdd".length()) {
return false;
}
int date;
try {
date = Integer.parseInt(dateString);
} catch (NumberFormatException e) {
return false;
}
int year = date / 10000;
int month = (date % 10000) / 100;
int day = date % 100;
// leap years calculation not valid before 1581
boolean yearOk = (year >= 1581) && (year <= 2500);
boolean monthOk = (month >= 1) && (month <= 12);
boolean dayOk = (day >= 1) && (day <= daysInMonth(year, month));
return (yearOk && monthOk && dayOk);
}
private static int daysInMonth(int year, int month) {
int daysInMonth;
switch (month) {
case 1: // fall through
case 3: // fall through
case 5: // fall through
case 7: // fall through
case 8: // fall through
case 10: // fall through
case 12:
daysInMonth = 31;
break;
case 2:
if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) {
daysInMonth = 29;
} else {
daysInMonth = 28;
}
break;
default:
// returns 30 even for nonexistant months
daysInMonth = 30;
}
return daysInMonth;
}
П.С. Приведенный выше пример метода вернет true
для «99999999». Мой вернет true только для существующих дат:).
У меня есть чудесное продолжение предложения SilentGhost - размещение отдельного ответа, так как поля комментария будут слишком узкими, чтобы содержать код :-)
itertools. Перестановки
встроены (начиная с 2.6) и быстры. Нам просто нужно условие фильтрации, которое для каждого (perm, perm[::-1]) будет принимать ровно одно из них. Так как в ОП сказано, что элементы всегда разные, мы можем просто сравнить любые 2 элемента:
for p in itertools.permutations(range(3)):
if p[0] < p[-1]:
print p
которые печатают:
(0, 1, 2)
(0, 2, 1)
(1, 0, 2)
Это работает, потому что обратная перестановка всегда перевернет отношение!
p[0] < p[1]
или любая другая пара также будет работать, так что у вас также есть некоторый контроль над тем, какую половину перестановки вы получите.
Я не уверен, есть ли более эффективный способ фильтрации. itertools.permutations
гарантирует лексикографический порядок, но лексикографическая позиция p
и p[::-1]
связаны довольно сложным образом. В частности, просто остановка на середине не работает.
Но я подозреваю (не проверял), что встроенный итератор с фильтрацией 2:1 превзойдет любую пользовательскую реализацию. И, конечно же, он выигрывает по простоте!
Если вы генерируете перестановки в лексикографическом порядке, вам не нужно ничего хранить, чтобы определить, наблюдалась ли уже обратная перестановка. Вам просто нужно лексикографически сравнить его с его реверсом - если он меньше, верните его, если больше, то пропустите.
Вероятно, есть более эффективный способ сделать это, но он прост и имеет требуемые свойства (реализуемый в качестве генератора использует O (n) оперативной памяти).
Это более краткая и быстрая версия принятого ответа ChristopheD, который мне очень понравился. Рекурсия отличная. Я заставил его обеспечить уникальность входящего списка, удалив дубликаты, однако, возможно, вместо этого следует просто вызвать исключение.
def fac(x):
return (1 if x==0 else x * fac(x-1))
def permz(plist):
plist = sorted(set(plist))
plen = len(plist)
limit = fac(plen) / 2
counter = 0
if plen==1:
yield plist
else:
for perm in permz(plist[1:]):
for i in xrange(plen):
if counter == limit:
raise StopIteration
counter += 1
yield perm[:i] + plist[0:1] + perm[i:]
# ---- testing ----
plists = [
list('love'),
range(5),
[1,4,2,3,9],
['a',2,2.1],
range(8)]
for plist in plists:
perms = list(permz(plist))
print plist, True in [(list(reversed(i)) in foo) for i in perms]
Как насчет этого:
from itertools import permutations
def rev_generator(plist):
reversed_elements = set()
for i in permutations(plist):
if i not in reversed_elements:
reversed_i = tuple(reversed(i))
reversed_elements.add(reversed_i)
yield i
>>> list(rev_generator([1,2,3]))
[(1, 2, 3), (1, 3, 2), (2, 1, 3)]
Кроме того, если возвращаемое значение должно быть списком, вы можете просто изменить yield i на yield list (i), но для целей итерации кортежи будут работать нормально.
РЕДАКТИРОВАТЬ : полностью изменено, чтобы сохранить все как генератор (а не весь список в памяти). Должен соответствовать требованиям (вычисляет только половину возможных перестановок (не обратных). EDIT2 : добавлена более короткая (и более простая) факториальная функция из сюда .
EDIT3: : (см. Комментарии) - версию с улучшениями можно найти в Версия bwopah .
def fac(x):
return (1 if x==0 else x * fac(x-1))
def all_permutations(plist):
global counter
if len(plist) <=1:
yield plist
else:
for perm in all_permutations(plist[1:]):
for i in xrange(len(perm)+1):
if len(perm[:i] + plist[0:1] + perm[i:]) == lenplist:
if counter == limit:
raise StopIteration
else:
counter = counter + 1
yield perm[:i] + plist[0:1] + perm[i:]
counter = 0
plist = ['a','b','c']
lenplist = len(plist)
limit = fac(lenplist) / 2
all_permutations_gen = all_permutations(plist)
print all_permutations_gen
print list(all_permutations_gen)
это реализация одного из предложений
из http: //en.wikipedia .org / wiki / Permutation # Lexicographic_order_generation Следующий алгоритм лексикографически генерирует следующую перестановку после данной перестановки. Он изменяет данную перестановку на месте.
, функция:
def perms(items):
items.sort()
yield items[:]
m = [len(items)-2] # step 1
while m:
i = m[-1]
j = [ j for j in range(i+1,len(items)) if items[j]>items[i] ][-1] # step 2
items[i], items[j] = items[j], items[i] # step 3
items[i+1:] = list(reversed(items[i+1:])) # step 4
if items<list(reversed(items)):
yield items[:]
m = [ i for i in range(len(items)-1) if items[i]<items[i+1] ] # step 1
проверка нашей работы:
>>> foo=list(perms([1,3,2,4,5]))
>>> True in [(list(reversed(i)) in foo) for i in foo]
False
Вот код, который помогает. Чтобы избавиться от дублирования, я заметил, что для вашего списка, если значение первого местоположения больше, чем значение последнего местоположения, то это будет дублирование. Я создаю карту, чтобы отслеживать, где каждый элемент был в списке для начала, а затем использую эту карту для выполнения теста. Код также не использует рекурсию, поэтому занимает мало места в памяти. Также список может состоять из элементов любого типа, а не только чисел, см. Последние два тестовых примера.
Вот код.
class Permutation:
def __init__(self, justalist):
self._data = justalist[:]
self._len=len(self._data)
self._s=[]
self._nfact=1
self._map ={}
i=0
for elem in self._data:
self._s.append(elem)
self._map[str(elem)]=i
i+=1
self._nfact*=i
if i != 0:
self._nfact2=self._nfact//i
def __iter__(self):
for k in range(self._nfact):
for i in range(self._len):
self._s[i]=self._data[i]
s=self._s
factorial=self._nfact2
for i in range(self._len-1):
tempi = (k // factorial) % (self._len - i)
temp = s[i + tempi]
for j in range(i + tempi,i,-1):
s[j] = s[j-1]
s[i] = temp
factorial //= (self._len - (i + 1))
if self._map[str(s[0])] < self._map[str(s[-1])]:
yield s
s=[1,2]
print("_"*25)
print("input list:",s)
for sx in Permutation(s):
print(sx)
s=[1,2,3]
print("_"*25)
print("input list:",s)
for sx in Permutation(s):
print(sx)
s=[1,2,3,4]
print("_"*25)
print("input list:",s)
for sx in Permutation(s):
print(sx)
s=[3,2,1]
print("_"*25)
print("input list:",s)
for sx in Permutation(s):
print(sx)
s=["Apple","Pear","Orange"]
print("_"*25)
print("input list:",s)
for sx in Permutation(s):
print(sx)
s=[[1,4,5],"Pear",(1,6,9),Permutation([])]
print("_"*25)
print("input list:",s)
for sx in Permutation(s):
print(sx)
и вот результат моих тестов.
_________________________
input list: [1, 2]
[1, 2]
_________________________
input list: [1, 2, 3]
[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
_________________________
input list: [1, 2, 3, 4]
[1, 2, 3, 4]
[1, 2, 4, 3]
[1, 3, 2, 4]
[1, 3, 4, 2]
[1, 4, 2, 3]
[1, 4, 3, 2]
[2, 1, 3, 4]
[2, 1, 4, 3]
[2, 3, 1, 4]
[2, 4, 1, 3]
[3, 1, 2, 4]
[3, 2, 1, 4]
_________________________
input list: [3, 2, 1]
[3, 2, 1]
[3, 1, 2]
[2, 3, 1]
_________________________
input list: ['Apple', 'Pear', 'Orange']
['Apple', 'Pear', 'Orange']
['Apple', 'Orange', 'Pear']
['Pear', 'Apple', 'Orange']
_________________________
input list: [[1, 4, 5], 'Pear', (1, 6, 9), <__main__.Permutation object at 0x0142DEF0>]
[[1, 4, 5], 'Pear', (1, 6, 9), <__main__.Permutation object at 0x0142DEF0>]
[[1, 4, 5], 'Pear', <__main__.Permutation object at 0x0142DEF0>, (1, 6, 9)]
[[1, 4, 5], (1, 6, 9), 'Pear', <__main__.Permutation object at 0x0142DEF0>]
[[1, 4, 5], (1, 6, 9), <__main__.Permutation object at 0x0142DEF0>, 'Pear']
[[1, 4, 5], <__main__.Permutation object at 0x0142DEF0>, 'Pear', (1, 6, 9)]
[[1, 4, 5], <__main__.Permutation object at 0x0142DEF0>, (1, 6, 9), 'Pear']
['Pear', [1, 4, 5], (1, 6, 9), <__main__.Permutation object at 0x0142DEF0>]
['Pear', [1, 4, 5], <__main__.Permutation object at 0x0142DEF0>, (1, 6, 9)]
['Pear', (1, 6, 9), [1, 4, 5], <__main__.Permutation object at 0x0142DEF0>]
['Pear', <__main__.Permutation object at 0x0142DEF0>, [1, 4, 5], (1, 6, 9)]
[(1, 6, 9), [1, 4, 5], 'Pear', <__main__.Permutation object at 0x0142DEF0>]
[(1, 6, 9), 'Pear', [1, 4, 5], <__main__.Permutation object at 0x0142DEF0>]
Вот моя реализация:
a = [1,2,3,4]
def p(l):
if len(l) <= 1:
yield l
else:
for i in range(len(l)):
for q in p([l[j] for j in range(len(l)) if j != i]):
yield [l[i]] + q
out = (i for i in p(a) if i < i[::-1])
P-функция является регулярной функцией permu, дает все возможности. Фильтр выполняется при повторении результата. Проще говоря, у этого есть два возможных результата: меньшая половина всей химической завивки и большая половина химической завивки. В этом примере выход содержит меньшую половину списка.
меньшая половина всей химической завивки и большая половина химической завивки. В этом примере выход содержит меньшую половину списка. меньшая половина всей химической завивки и большая половина химической завивки. В этом примере выход содержит меньшую половину списка. itertools.permutations
делает именно то, что вы хотите. вы можете использовать обратный
встроенный , а также
Сначала немного кода установки:
try:
from itertools import permutations
except ImportError:
# straight from http://docs.python.org/library/itertools.html#itertools.permutations
def permutations(iterable, r=None):
# permutations('ABCD', 2) --> AB AC AD BA BC BD CA CB CD DA DB DC
# permutations(range(3)) --> 012 021 102 120 201 210
pool = tuple(iterable)
n = len(pool)
r = n if r is None else r
if r > n:
return
indices = range(n)
cycles = range(n, n-r, -1)
yield tuple(pool[i] for i in indices[:r])
while n:
for i in reversed(range(r)):
cycles[i] -= 1
if cycles[i] == 0:
indices[i:] = indices[i+1:] + indices[i:i+1]
cycles[i] = n - i
else:
j = cycles[i]
indices[i], indices[-j] = indices[-j], indices[i]
yield tuple(pool[i] for i in indices[:r])
break
else:
return
def non_reversed_permutations(iterable):
"Return non-reversed permutations for an iterable with unique items"
for permutation in permutations(iterable):
if permutation[0] < permutation[-1]:
yield permutation