Перейти к содержимому

Фото
- - - - -

Функция валидации исикукода


  • Вы не можете создать новую тему
  • Please log in to reply
21 ответов в этой теме

#1 Setor

Setor
  • Постоялец
  • 1 890 сообщений
  • Откуда:Эстония, Таллин

Отправлено 27 мая 2009 - 19:15

Набрёл сегодня в Wikipedia на: Isikukood, где был представлен ужасный пример валидации исикукода, который нужно обновить и решил выложить свой вариант, но хотелось бы найти более идеальное решение, если оно существует.
Мой вариант:
function getIsikukoodControllNumber( $isikukood )
{
  $isikukood = (string) $isikukood; // Эту строку можно опустить, т.к. PHP сам приведёт тип к стрингу

  // $k - начало ряда
  // цикл делает максимум 2 итерации
  for ( $k = 1; $k <= 3; $k += 2 )
  {
	$j = $k;
	$s = 0; // сумма

	// $i - порядковый номер цифры в исикукоде
	for ( $i = 0; $i < 10; ++$i )
	{
	  $s += $j * $isikukood{$i};
	  $j  = ( 9 == $j ? 1 : $j + 1 );
	}

	if ( ( $s %= 11 ) < 10 )
	{
	  return $s;
	}
  }

  return 0;
}

Спецификация из вики:

Kontrollnumber arvutatakse järgnevalt:

1) isikukoodi numbritele esimene kuni eelviimane (s.t. välja arvatud kontrolljärgu koht) seatakse vasakult paremale kaalud 1, 2, 3, 4, 5, 6, 7, 8, 9, 1;
2) numbrid korrutatakse kaaludega ja saadud tulemused liidetakse kokku;
3) leitakse saadud summale (2) jääk 11-ga jagamisel;
4) kui tulemus (3) on alla 10, siis on see kontrollnumbriks;
5) kui tulemus (3) on võrdne 10, siis asendatakse kaalud 3, 4, 5, 6, 7, 8, 9, 1, 2, 3 ning korratakse arvutust;
6) kui tulemus (5) on alla 10, siis on see kontrollnumbriks;
7) kui tulemus (5) on võrdne 10, siis kontrollnumbriks on 0.


Пример кода из википедии
<?php
$isikukood = '3760503029';
function isikukoodi_kontrollsumma($isikukood)
{
	for($j = 1; $j <= 9; $j++) $summa += $j * $isikukood[$j-1];
	$summa += $isikukood[9]; $summa %= 11;
 
	if($summa < 10 && $isikukood[10] == $summa) return $summa;
	if($summa == 10)
	{
		$summa = 0;
		for($j = 3, $i = 0; $j <= 9; $j++, $i++) $summa += $j * $isikukood[$i];
		for($j = 1; $j <= 3; $j++, $i++) $summa += $j * $isikukood[$i];
		$summa %= 11;
		if($summa < 10 && $isikukood[10] == $summa) return $summa;
				if($summa == 10) return 0;
	}
	return $summa;
}
 
echo isikukoodi_kontrollsumma($isikukood);
?>

if($summa < 10 && $isikukood[10] == $summa) return $summa;

Зачем тут эта проверка (2е выражение), по-моему это отклонение от спецификации.

Может будут более изящные примеры на других языках? :)

Сообщение изменено: Setor (27 мая 2009 - 19:16 )

  • 0

#2 Cryptoboy

Cryptoboy
  • Пользователь
  • 280 сообщений

Отправлено 27 мая 2009 - 21:37

Мое, на Java , с рабочего проекта :
int getChecksum(String s) {
		int sum = 0;
		for (int i = 0; i < 10; i++) {
			sum += Character.getNumericValue(s.charAt(i)) * (i % 9 + 1);
		}
		int r = sum % 11;
		if (r < 10) {
			return r;
		}
		else {
			sum = 0;
			for (int i = 0; i < 10; i++) {
				sum += Character.getNumericValue(s.charAt(i)) * ((i + 2) % 9 + 1);
			}
			r = sum % 11;
			if (r < 10) {
				return r;
			} else {
				return 0;
			}
		}
	}

  • 0

#3 Setor

Setor
  • Постоялец
  • 1 890 сообщений
  • Откуда:Эстония, Таллин

Отправлено 27 мая 2009 - 23:12

Slаm, дублирование кода не есть гуд... почти 1 в 1 с wiki :)
  • 0

#4 Cryptoboy

Cryptoboy
  • Пользователь
  • 280 сообщений

Отправлено 27 мая 2009 - 23:39

Чесно не вижу ничего похожего .. =) Да, делал по алгоритму что там написан, но код не читал, никогде не переписываю чужой код :)
  • 0

#5 еть.

еть.
  • Постоялец
  • 2 655 сообщений

Отправлено 27 мая 2009 - 23:50

function getIsikukoodControllNumber( $isikukood )
{
  $isikukood = (string) $isikukood; // Эту строку можно опустить, т.к. PHP сам приведёт тип к стрингу

  // $k - начало ряда
  // цикл делает максимум 2 итерации
  for ( $k = 1; $k <= 3; $k += 2 )
  {
	$s = 0; // сумма

	// $i - порядковый номер цифры в исикукоде
	for ( $i = 0; $i < 10; ++$i )
	{
	  $s += $k * $isikukood{$i};
	  $k  = ( 9 == $k ? 1 : $k + 1 );
	}

	if ( ( $s %= 11 ) < 10 )
	{
	  return $s;
	}
  }

  return 0;
}
Setor, твой код отлично работает и без объявления j.
  • 0

– Совсем худо, – заключил хозяин, – что-то, воля ваша, недоброе таится в мужчинах, избегающих вина, игр, общества прелестных женщин, застольной беседы. Такие люди или тяжко больны, или втайне ненавидят окружающих.


#6 MiamiBC

MiamiBC
  • Пользователь
  • 21 сообщений

Отправлено 28 мая 2009 - 01:22

Не знаю, Setor что есть дублирование - плохо ли, или может это всего лишь закрепление пройденного? Тем более человек сказал что написал на яве, ты же сам просил изящные примеры на других языках ;)

В задаче координально нового врятли что получится придумать, можно лишь слегка упростить вычисление. Во-первых, как мне показалось, второй цикл вычислений делать ни к чему (тестил на кучке исикукодов своих друзей-знакомых) всё вычислялось за первый проход. Если затестить на большом кол-ве кодов, то можно из следующей функции удалить половину:

function getIsikukoodChecksum($i) {
  $s = $i[0]*1+$i[1]*2+$i[2]*3+$i[3]*4+$i[4]*5+$i[5]*6+$i[6]*7+$i[7]*8+$i[8]*9+$i[9]*1;
  if ( ( $s %= 11 ) < 10 ) return $s;
  $s = $i[0]*3+$i[1]*4+$i[2]*5+$i[3]*6+$i[4]*7+$i[5]*8+$i[6]*9+$i[7]*1+$i[8]*2+$i[9]*3;
  if ( ( $s %= 11 ) < 10 ) return $s;
  return 0;
}
Изменил последнюю строчку, спасибо Setor за поправочку.

Сообщение изменено: MiamiBC (28 мая 2009 - 18:16 )

  • 0
_   _ __ ____ _____/\  ____________ ____ __  __ _  _
www.blackcrystal.net \/ Show what You can. Learn what You don't.

#7 ParadoxL

ParadoxL
  • Постоялец
  • 5 023 сообщений
  • Откуда:Edinburg

Отправлено 28 мая 2009 - 06:29

Ну раз все показывают ... дайте я тоже покажу.
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;

import java.util.regex.Pattern;

public class PersonalCode {

  /** Each Estonian personal code must match this pattern */
  private static final Pattern NUMBER_PATTERN = Pattern.compile("^\\d{11}$");

  /**
	 * Checks if is valid.
	 * 
	 * @param number
	 *			the number
	 * 
	 * @return true, if is valid
	 */
  public static boolean isValid(final String number) {
	return StringUtils.isNotBlank(number)
		   && NUMBER_PATTERN.matcher(number).matches()
		   && calculateChecksum(number) == NumberUtils.toInt(number.substring(10));
  }


  /**
	 * Calculates checksum number for a given personal code. By the general rule
	 * calculated checksum must be the same as the last digit of the given
	 * personal code
	 * 
	 * @param number
	 *			personal code as string
	 * 
	 * @return registration number checksum
	 */
  private static int calculateChecksum(String number) {
	int remainder = calculateWeightRemainder(number, new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 1});
	if (remainder < 10) 
	  return remainder;
	if (remainder == 10) 
	  remainder = calculateWeightRemainder(number, new int[]{3, 4, 5, 6, 7, 8, 9, 1, 2, 3});
	return remainder < 10 ? remainder : 0;
  }

  /**
	 * Calculate weight remainder.
	 * 
	 * @param number
	 *			the number
	 * @param weights
	 *			the weights
	 * 
	 * @return the int
	 */
  private static int calculateWeightRemainder(final String number, final int[] weights) {
	int sum = 0;
	for (int i = 0; i < weights.length; i++) {//sum up all weights
	  sum += NumberUtils.toInt(number.substring(i, i + 1)) * weights[i];
	}
	return sum % 11;//return division remainder
  }

}


Сообщение изменено: Incubo (28 мая 2009 - 06:30 )

  • 0
Victoria nulla est, Quam quae confessos animo quoque subjugat hostes ...
Верю в смерть после жизни, любовь после секса и в крем после бритья ...

#8 Setor

Setor
  • Постоялец
  • 1 890 сообщений
  • Откуда:Эстония, Таллин

Отправлено 28 мая 2009 - 11:40

Setor, твой код отлично работает и без объявления j.

На первой итерации работает, а на 2й - уже нет :)

Не знаю, Setor что есть дублирование - плохо ли, или может это всего лишь закрепление пройденного? Тем более человек сказал что написал на яве, ты же сам просил изящные примеры на других языках ;)

В задаче координально нового врятли что получится придумать, можно лишь слегка упростить вычисление. Во-первых, как мне показалось, второй цикл вычислений делать ни к чему (тестил на кучке исикукодов своих друзей-знакомых) всё вычислялось за первый проход. Если затестить на большом кол-ве кодов, то можно из следующей функции удалить половину:

function getIsikukoodChecksum($i) {
  $s = $i[0]*1+$i[1]*2+$i[2]*3+$i[3]*4+$i[4]*5+$i[5]*6+$i[6]*7+$i[7]*8+$i[8]*9+$i[9]*1;
  if ( ( $s %= 11 ) < 10 ) return $s;
  $s = $i[0]*3+$i[1]*4+$i[2]*5+$i[3]*6+$i[4]*7+$i[5]*8+$i[6]*9+$i[7]*1+$i[8]*2+$i[9]*3;
  if ( ( $s %= 11 ) < 10 ) return $s;
  return null;
}

Гениально, только маленькая поправка: в последней строчке return 0 а не null (по спецификации)

Анекдот из универа, учитель по программированию расказывала:
Дана задача: нахождение чексуммы исикукода
Решение:
1) студент 1го курса применил кучу циклов и if'ов, как в примере с википедии
2) студент 2го курса написал функцию(и)
3) студент 3го курса написал класс
и последняя фаза: "гуру" не стал заморачиваться и написал как miamiBc - тупо в лоб без циклов, без классов :) Причём, большинству такая идея не придёт в голову... гениально просто!
  • 0

#9 ParadoxL

ParadoxL
  • Постоялец
  • 5 023 сообщений
  • Откуда:Edinburg

Отправлено 28 мая 2009 - 12:10

Спасибо конечно за 3-й курс ... но насчет "гуру" не согласен ...
  • 0
Victoria nulla est, Quam quae confessos animo quoque subjugat hostes ...
Верю в смерть после жизни, любовь после секса и в крем после бритья ...

#10 defcon

defcon
  • Пользователь
  • 83 сообщений

Отправлено 28 мая 2009 - 13:06

Алгоритм в первую очередь должен хорошо читаться. Поэтому вариант с википедии лучше, чем тот, что с одним циклом.Вариант от miamiBC - лучший, так как полностью объясняет алгоритм и не вводит дополнительной сложности.

По поводу классов. Ну ка, насколько вероятно, что функция проверки личного кода изменится? По моему, это довольно стабильный код и нечего здесь навороты изобретать, позволяющие в будущем что-то изменить. Наша задача управлять сложностью, а не создавать себе на голову.

По поводу дублирования кода - дублирование кода плохо не потому что нужно писать дважды, а потому что такая же функциональность может потребоваться где-то еще. Ну ка, насколько вероятно, что функция расчета суммы весов потребуется еще где-то в проекте?
  • 0

#11 Setor

Setor
  • Постоялец
  • 1 890 сообщений
  • Откуда:Эстония, Таллин

Отправлено 28 мая 2009 - 13:09

Спасибо конечно за 3-й курс ... но насчет "гуру" не согласен ...

Это шутка была :) У меня тоже оформлено в виде класса с остальными проверками на валидность данных в исикукоде.

Просто зачастую есть простое решение, о котором ни кто не догадается, кроме некоторых людей... Не знаю что у них в голове :)
  • 0

#12 Equals

Equals
  • Пользователь
  • 76 сообщений

Отправлено 28 мая 2009 - 13:21

Ничего нового, просто на Ruby

def isikukoodValidation(isikukood)
  
  isikukood = isikukood.to_s

  2.times do |i|
	sum = 0
	j = 2 * i + 1
	10.times do |c|
	  sum += j * isikukood[c].chr.to_i
	  j  =  9 == j ? 1 : j + 1;
	end
	return sum unless !((sum %= 11) < 10)
  end
  nil
end

  • 0

#13 ParadoxL

ParadoxL
  • Постоялец
  • 5 023 сообщений
  • Откуда:Edinburg

Отправлено 28 мая 2009 - 14:27

По поводу дублирования кода - дублирование кода плохо не потому что нужно писать дважды, а потому что такая же функциональность может потребоваться где-то еще. Ну ка, насколько вероятно, что функция расчета суммы весов потребуется еще где-то в проекте?


Ну давай чисто по существу ... проект ... минфин ... запрос на плтежку ... запрос на проводку ... запрос на оплату ... запрос на запрос ... :) и Так далее ...
  • 0
Victoria nulla est, Quam quae confessos animo quoque subjugat hostes ...
Верю в смерть после жизни, любовь после секса и в крем после бритья ...

#14 shb

shb

    New life, much more options

  • Постоялец
  • 5 253 сообщений
  • Откуда:Таллинн

Отправлено 28 мая 2009 - 14:43

Incubo, а что, сунул эти пять строк в утиль и пользуйся :))
  • 0
Мыслящий человек просто обязан время от времени поднимать себя за волосы © Тот самый Мюнгхаузен

Joga Bonito!

#15 ParadoxL

ParadoxL
  • Постоялец
  • 5 023 сообщений
  • Откуда:Edinburg

Отправлено 28 мая 2009 - 14:48

shb, ну если ты незаметил ... то сам класс есть утиль ... все приваты только один паблик.... класс здесь хорошо тем что ... прекомпайлинг патрена и так далее ... так же хорошо для написанию юнитов длоя тестов ... да и еще несколько детских обид которые связаны с постановкой задачи.
  • 0
Victoria nulla est, Quam quae confessos animo quoque subjugat hostes ...
Верю в смерть после жизни, любовь после секса и в крем после бритья ...

#16 Setor

Setor
  • Постоялец
  • 1 890 сообщений
  • Откуда:Эстония, Таллин

Отправлено 28 мая 2009 - 15:24

Может глупый вопрос, а закрытые (protected/private) статические методы не есть плохо? Где-то недавно читал, что в некоторых языках они запрещены.

По поводу дублирования кода - дублирование кода плохо не потому что нужно писать дважды, а потому что такая же функциональность может потребоваться где-то еще. Ну ка, насколько вероятно, что функция расчета суммы весов потребуется еще где-то в проекте?

Сам факт копирования кода уже напрашивается на рефакторинг. Первая версия моего алгоритма была похожа на пример из вики (когда я её писал, примера в википедии ещё не было). Но как это странно не звучит, ясности она ни сколько не прибавила. Толчком для рефакторинга послужило желание уменьшить количество строк кода за счёт более сложной логики и убрать мозолящие глаза одинаковые строки.
  • 0

#17 shb

shb

    New life, much more options

  • Постоялец
  • 5 253 сообщений
  • Откуда:Таллинн

Отправлено 28 мая 2009 - 15:37

Setor, нет, не есть плохо. Надо просто правильно ими пользоваться.
  • 0
Мыслящий человек просто обязан время от времени поднимать себя за волосы © Тот самый Мюнгхаузен

Joga Bonito!

#18 defcon

defcon
  • Пользователь
  • 83 сообщений

Отправлено 28 мая 2009 - 15:52

проект ... минфин ... запрос на плтежку ... запрос на проводку ... запрос на оплату ... запрос на запрос

Incubo, мы об одном и том же говорим? Я говорю о calculateWeightRemainder() и прочих приватных методах.

Сам факт копирования кода уже напрашивается на рефакторинг.

Ну я не буду спорить. Лично мне, в данном конкретном случае, читать легче пример "в лоб", чем какие-то там псевдо-элегантные решения, что и должно быть целью. имхо.
  • 0

#19 Cryptoboy

Cryptoboy
  • Пользователь
  • 280 сообщений

Отправлено 28 мая 2009 - 16:25

Толчком для рефакторинга послужило желание уменьшить количество строк кода за счёт более сложной логики и убрать мозолящие глаза одинаковые строки.

Че за бред? Читабельность кода куда важнее, чем количество строк в нем, или ты не согласен?
  • 0

#20 BlackIce

BlackIce

    грозный Дон Пако

  • Пользователь
  • 313 сообщений
  • Откуда:Tallinn

Отправлено 10 июня 2009 - 15:42

Че за бред? Читабельность кода куда важнее, чем количество строк в нем, или ты не согласен?

Главное чтоб работало ...
  • 0
а кули, я тоже рульный дизайнер ввв.ме2.ее

#21 BlodwyN

BlodwyN
  • Пользователь
  • 40 сообщений

Отправлено 14 июня 2009 - 22:54

паттерн regex match "^(3|4)\\d{2}([0][1-9]|[1][0-2])((0|1|2)[0-9]|[3][0-1])\\d{4}$"

функция на яве ОДНОЙ строкой

public boolean isValidId(String idNum){ return idNum.matches("^(3|4)\\d{2}([0][1-9]|[1][0-2])((0|1|2)[0-9]|[3][0-1])\\d{4}$"); }

призы будут ? :)
  • 0

#22 dronius

dronius
  • Пользователь
  • 153 сообщений

Отправлено 14 июня 2009 - 23:02

не будут, твой регексп проверяет только формат, но не контрольную сумму
  • 0