Страницы

Поиск по вопросам

Показаны сообщения с ярлыком логика. Показать все сообщения
Показаны сообщения с ярлыком логика. Показать все сообщения

воскресенье, 15 марта 2020 г.

Сосчитать определенное количество элементов в группе

#java #логика


Есть матрица клеток определенного размера. Клетка имеет два состояния: занято и пусто.
Нужно сосчитать количество групп клеток и количество клеток в каждой группе. Группой
считаются рядом стоящие клетки, но не по диагонали. 



На рисунке 4 группы: в 1-ой группе 6 клеток, в остальных - по одной.

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

public int getNumberCells(final Cell cellOccupied) {
        int numberRows = field.getNumberRows();
        int numberColumns = field.getNumberColumns();
        int count = 0;

        for (int i = 0; i < numberRows; i++) {
            for (int j = 0; j < numberColumns; j++) {
                if (field.getCell(i, j) == field.getCell(i, j + 1) &&
                        field.getCell(i, j) == cellOccupied) {
                            count++;
                }

                if (field.getCell(i, j) == field.getCell(i + 1, j) &&
                        field.getCell(i, j) == cellOccupied) {
                            count++;
                }
            }
       }
       return count;
}

    


Ответы

Ответ 1



Для поиска групп используем классический алгоритм заливки. public class Test { static final int columns = 8; static final int rows = 3; static int[][] matrix = { { 0,0,1,1,1,0,1,0 }, { 0,1,1,0,0,0,0,1 }, { 0,1,0,0,0,0,1,0 } }; static int floodFill(int row, int col) { if (row < 0 || col < 0 || col >= columns || row >= rows) return 0; if (matrix[row][col] != 1) return 0; matrix[row][col] = 2; return 1 + floodFill(row + 1, col) + floodFill(row - 1, col) + floodFill(row, col + 1) + floodFill(row, col - 1); } public static void main(String []args) { int count = 0; for (int row = 0; row < rows; row++) { for (int col = 0; col < columns; col++) { if (matrix[row][col] == 1) { count++; System.out.println(floodFill(row, col)); } } } System.out.println(count); } } Update: добавил подсчёт количества элементов в группе.

Ответ 2



Однопроходный алгоритм без рекурсий: Перебираем элементы слева-направо и сверху-вниз. Встретив значение 1, заменяем его, к примеру, на 11. Далее в процессе перебора найдя значение 1, которое граничит с 11, заменяем на 11, а если не граничит, то на 12 и т.д. В результате, именуем каждую группу своим уникальным числом. Параллельно в отдельном массиве считаем сколько раз мы меняли 1 на значение именованной группы. Скорее всего, возникнут конфликты вроде того, что одной группе были присвоены разные «имена». Необходимо параллельно определять такие конфликты. Конфликт разрешается путем ведения третьего массива «синонимов». Если мы имеем 1, которая граничит например с 11 и 12, то считаем, что 11 и 12 это одна и та же группа. Код осуществляющий алгоритм: public class Test3 { public static void main(String[] args) { int[][] matrix = { { 0,1,1,0,1,0,0,1 }, { 0,0,1,1,1,1,0,1 }, { 1,1,0,1,0,0,1,1 }, { 1,0,1,0,1,1,0,1 } }; getMatrixGroupsFrom01( matrix ); } public static void getMatrixGroupsFrom01( int[][] matrix ) { int width = matrix[0].length; int height = matrix.length; int groupsCount = 10; // first group - 11, second - 12 ... int group1, group2; HashMap groupsLength = new HashMap(); HashMap> synonims = new HashMap>(); List currentSyn; for ( int i = 0; i < width; i++ ) { for ( int j = 0; j < height; j++ ) { if ( matrix[j][i] == 1 ) { // если текущая ячейка принадлежит пока неизвестной группе group1 = group2 = 0; // сброс значений if ( j>0 && matrix[j-1][i]>0 ) group1 = matrix[j-1][i]; // группа сверху if ( i>0 && matrix[j][i-1]>0 ) group2 = matrix[j][i-1]; // группа снизу if ( group1 == 0 && group2 == 0 ) { // если окружают только нули, то надо задать новую группу groupsCount++; // новое значение группы matrix[j][i] = groupsCount; groupsLength.put( groupsCount, 1 ); } else if ( group1 == 0 || group2 == 0 || group1 == group2 ) { // если группы одинаковы или одна из них равна 0 matrix[j][i] = group1 == 0 ? group2 : group1; groupsLength.put( matrix[j][i], groupsLength.get( matrix[j][i] )+1 ); // инкремент } else { // если группы разные, то есть конфликт. Конфликт нужно разрешить создав синоним if ( group1 > group2 ) { // чтобы работать потом с минимальным значением int swap = group2; group2 = group1; group1 = swap; } //System.out.println( group1 + " " + group2 ); if ( synonims.containsKey( group1 ) ) { currentSyn = synonims.get( group1 ); } else { currentSyn = new ArrayList(); } if ( currentSyn.indexOf( group2 ) == -1 ) { // если такого синонима еще нет currentSyn.add( group2 ); } synonims.put(group1, currentSyn); // создаем синоним matrix[j][i] = group1; groupsLength.put( group1, groupsLength.get( group1 )+1 ); // инкремент } } System.out.print( matrix[j][i] + "\t" ); } System.out.print( "\n" ); } // с этого момента есть исчерпывающие данные о группах int synSize = synonims.size(); Integer[] s = {}; s = synonims.keySet().toArray( s ); for ( int i=0; i < synSize; i++ ) { // перебираем синонимы System.out.print( "\n" + s[i] + " => " ); List sd = synonims.get( s[i] ); Iterator cs = sd.iterator(); while ( cs.hasNext() ) { int synVal = cs.next(); System.out.print( synVal + " " ); groupsLength.put( s[i], groupsLength.get( s[i] ) + groupsLength.get( synVal ) ); // добавляем к основной сумме сумму синонима groupsLength.remove( synVal ); // удаляем синонимичную сумму } } System.out.println( "\n" ); s = groupsLength.keySet().toArray( s ); for ( int i = 0; i < s.length; i++ ) { System.out.println( "Group:\t" + s[i] + "\tSize: " + groupsLength.get( s[i] ) ); } } } Результат работы: 0 0 11 11 12 0 11 0 12 12 0 13 0 12 12 0 14 12 0 15 0 12 0 15 0 0 16 0 17 17 16 16 16 => 17 12 => 14 Group: 16 Size: 5 Group: 11 Size: 3 Group: 12 Size: 8 Group: 13 Size: 1 Group: 15 Size: 2 Чтобы навести красоту внутри матрицы, нужен еще один проход, в котором синонимы заменяются одним значением, но для задач, поставленных в вопросе, красота не нужна.

понедельник, 24 февраля 2020 г.

Раскрыть скобки (a+b)%c

#математика #логика


Можно ли преобразовать (a+b)%c к сумме слагаемых, каждое из которых не содержит a
и b одновременно?
    


Ответы

Ответ 1



Операция % дистрибутивна (a + b)%c = (a%c + b%c)%c

Ответ 2



Можно в случае a + b < cтогда подходят a и b по отдельности. Иначе - нельзя.

Ответ 3



На самом деле мне нужно выразить i-тый бит суммы чисел (a+b). ((a+b)>>i)&1 ((a+b)/pow(2,i))%2

вторник, 31 декабря 2019 г.

Запрет на многократный вызов события при действиях с мышью

#c_sharp #события #логика #xna


Всем доброго времени суток!

Пишу собственный GUI-интерфейс для игрушки. 

Класс Button.

Имеется следующий набор событий-делегатов:

public event EventHandler MouseUpHandler;
public event EventHandler MouseDownHandler;
public event EventHandler MouseOutHandler;
public event EventHandler MouseInHandler;


Каждому из событию соответствует свой метод:

private void OnMouseIn() {...}
private void OnMouseOut() {...}
private void OnMouseUp() {...}
private void OnMouseDown() {...}


В них выполняется примерно такой код:

private void OnMouseDown()
{
    EventHandler tempHandler = MouseDownHandler; // получаем делегат события
    if (tempHandler != null) // проверяем, не пустой ли делегат
    {
        tempHandler(this, EventArgs.Empty); // вызываем событие
    }
    _state = ButtonState.Click; // используется для определения правильных координат
на спрайте текстуры кнопки во время ее рисования
}


Соответствующие методы присутствуют для других событий-делегатов.
В этом же классе присутствует метод 
Update(), который просчитывает логику кода:

public void Update()
{
     /* Формируем данные о положении мыши и о зоне пересечения (на основе позиции
и размеров кнопки) */
     MouseState mouseState = Mouse.GetState();
     Point mousePosition = new Point(mouseState.X, mouseState.Y); 

     Rectangle buttonRectangle = new Rectangle
     (
        (int) this.Position.X, (int) this.Position.Y,
        (int) this.Size.X, (int) this.Size.Y
     );

     if (buttonRectangle.Contains(mousePosition)) // проверяем на наличие пересечения
курсора мыши и кнопки
     {
         if (mouseState.LeftButton == Microsoft.Xna.Framework.Input.ButtonState.Pressed)
//ЛКМ - нажатие кнопки мыши
         {
             OnMouseDown();
         }
         if (_mousePrevState.LeftButton == Microsoft.Xna.Framework.Input.ButtonState.Pressed 
         && mouseState.LeftButton == Microsoft.Xna.Framework.Input.ButtonState.Released)
// ЛКМ - отпускание кнопки мыши
         {
             OnMouseUp();
         }
     } else // выход курсора за границы кнопки
     {
         OnMouseOut();
     }
    _mousePrevState = mouseState; // сохраняем предыдущее состояние (MouseUp может
быть только после MouseDown)
}


В другом месте создается экземпляр кнопки:

Button button = new Button(...);


Соответственно задаются свойства, текстуры и т.д. Все это функционирует. 
Далее, в том же "другом месте", делегату присваивается метод:

button.MouseInHandler += Название_метода;


С этим, думаю, все должно быть понятно. 
Логика вкратце - создается объект, ему в специальный делегат присваивается метод,
после чего в методе 
Update() проверяется, было ли пересечение кнопки и курсора, был ли клик и т.д. и
на основе этого уже вызывается нужное нам событие. 

Проблема, которая имеется сейчас: метод вызывается не единожды, а множество раз подряд.

Все это приводит к тому, что, например, вывод в консоль любого текста происходит
множество раз, но требуется не более одного. 

Я попробовал решить проблему с помощью дополнительных полей:

private bool _isMouseUp;
private bool _isMouseDown;
private bool _isMouseIn;
private bool _isMouseOut;


С изменением структуры логики методов-событий:

private void OnMouseIn()
{
    if (!_isMouseIn) // если событие не вызывалось 
    {
         EventHandler tempHandler = MouseInHandler;
         if (tempHandler != null)
         {
              tempHandler(this, EventArgs.Empty);
         }
         _isMouseIn = true; // определяем событие, как вызванное и не даем ему совершиться
повторно
         _isMouseOut = false; // после In события можно допустить выполнение Out события
    }
    _state = ButtonState.Hover;
}
// примерно такой же код ниже, разве что теперь Out и In поменялись местами
private void OnMouseOut()
{
    if (!_isMouseOut)
    {
         EventHandler tempHandler = MouseOutHandler;
         if (tempHandler != null)
         {
              tempHandler(this, EventArgs.Empty);
         }
         _isMouseOut = true;
         _isMouseIn = false;
    }
    _state = ButtonState.Normal;
}


Для MouseDown и MouseUp соответствующие изменения. 

И все это работает, до тех пор, пока пользователь ведет себя нормально. Однако если
пробовать различные ситуации, например, зажать ЛКМ, после чего навести на кнопку и
отпустить, или зажать ЛКМ над кнопкой и отпустить ее в другом месте - все это приводит
к тому, что в какой-либо момент то или иное событие, которое должно было быть вызвано
- не вызывается, либо наоборот, вызывается то событие, которые вызываться не должно. 

Я попробовал как можно более подробно описать свою проблему, но если будут какие-нибудь
вопросы - задавайте, уточню, если нужно.
    


Ответы

Ответ 1



Обычно для UI-элементов вводят понятие Capture. Когда происходит событие MouseDown, текущий элемент под мышью фиксируется. Эта фиксация и называется capture. После этого все события о перемещении мыши доставляются только этому элементу. При этом событие отпускания мыши тянет за собой автогенерацию клика только если при этом мышь была внутри элемента с capture. После отпускания мыши capture обнуляется. Таким образом исчезают неожиданные комбинации событий. Вы можете проверить работу capture на окне какого-нибудь приложения: нажмите на крестик закрытия окна в правом верхнем углу, и, не отпуская кнопку мыши, уведите её из кнопки. Теперь отпустите мышь, крестик не сработает. Кстати, у вас в коде логическая ошибка: для MouseDown нужно проверять предыдущее состояние точно так же, как вы делаете для MouseUp: MouseDown происходит только если предыдущее состоянии мыши было ненажатым.

Как поступит Java при компиляции?

#логика #java #компиляция


Код:
int x = 1;
if (x != 2) { int y = 2;}
int z = x + y;
System.out.print(z);

Собственно говоря, этот код, несмотря на if, в любом случае эквивалентен этому:
System.out.print(3);

А теперь сам вопрос Что запишет в скомпилированный файл java? Короткую или длинную
версию кода?    


Ответы

Ответ 1



Маленький эксперимет. Вариант 1 public static void main(String[] args) { int x = 1; int y = 0; if (x != 2) { y = 2;} int z = x + y; System.out.print(z); } Никаких изменений. 1 в 1. Вариант 2 Исходник: public static void main(String[] args) { int x = 1 + 4; int y = 0 + x; if (x != 2) y = 2; int z = x + y - y + y - x + x; System.out.print(z); } декомпиляция: public static void main(String[] args) { int x = 5; int y = 0 + x; if (x != 2) y = 2; int z = x + y - y + y - x + x; System.out.print(z); } Как и ожидалось, ушло только очевидное сложение. Вариант 3 А вот с константами всё интереснее: Исходник: public static void main(String[] args) { final int x = 1 + 4; final int y = 1 + x; final int z = x + y - y + y - x + x; System.out.print(z); } Декомпиляция: public static void main(String[] args) { int x = 5; int y = 6; int z = 11; System.out.print(11); } Вот тут уже оптимизатор оттянулся как захотел) Инструменты JDK 1.7.0.9 + JD 0.6.2

воскресенье, 29 декабря 2019 г.

Как работает оператор присваивания c типом boolean в Java?

#java #логика


Есть код. 

  public class IfElseTest {
    public static void main(String...args) {
      boolean b = false;
      if (b == false) {
        System.out.println(b=false);
      }

      if (b = false) {
        System.out.println("if statement");
      } else {
        System.out.println("else statement");
      }
    }
  }


На выходе эта программа даёт:


false 
else statement


И неясно, почему выражение в пятой строчке   System.out.println(b=false);компилятор
толкует как false. Хотя интуитивно ясно, что если b был ложь, то b присвоить ложь должно
быть true.

Вопрос: почему b = false компилятор воспринимает как false?
    


Ответы

Ответ 1



Хорошее форматирование кода помогает лучше его понять.:) Поэтому я запишу код в следующем виде boolean b = false; if (b == false) System.out.println(b=false); if (b = false) System.out.println("if statement"); else System.out.println("else statement"); В первом if-предложении if (b == false) System.out.println(b=false); переменная b сравнивается с литералом false, и так как эта переменная была инициализирована этим же значением boolean b = false; то очевидно результат вычисления выражения if-предложения, то есть результат сравнения, является истина. Поэтому на консоль выводится System.out.println(b=false); Во втором if-else-предложении if (b = false) System.out.println("if statement"); else System.out.println("else statement"); в условии if переменной b присваивается значение false if (b = false) результатом этого выражения, то есть присваивания, является результат присваивания**. Так как результат равен false, то кодовый блок после if-предложения не будет выполнен, а управление перейдет на предложение с else else System.out.println("else statement"); и, соответственно на консоль будет выдано System.out.println("else statement"); Если сравнить эти два if-предложения if (b == false) if (b = false) то в первом if-предложении значением выражения является результат сравнения. Так как false (то есть b) равно самому себе, то результат сравнения истина. Во-втором if-предложении значением выражения является результат присваивания переменной b значения false, то есть значение выражения if-предложения есть false. Вы могли бы переписать второе if-предложение следующим образом if (( b = false) == false ) и тогда, фактически, оно стало бы эквивалентно предложениям из начала программы boolean b = false; if (b == false) И вы бы получили тот же самый результат, что и в первом if-предложении, так как значением выражения стало не присвоенное значение переменной b, а результат сравнения.

Ответ 2



Не могу сказать умных слов. Но интуитивно понятно, что System.out.println(b=false); это эквивалент b=false; System.out.println(b); Т.е. сначала обрабатывается операция присваивания, а потом выводится значение переменной. Т.е. само по себе присваивание не является булевой операцией. Можете ради интереса проверить с другими типами, по идее должно вывести 10: int a=5; System.out.println(a = 10); PS не относится непосредственно к делу, но я бы рекомендовал поправить форматирование, а то у меня есть предположение, судя по Вашим отступам, что Вы в целом можете еще и немного неправильно интерпретировать операции, которые выполняются в случае выполнения первого условия.

Ответ 3



В первом случае вы же сравниваете переменную с константой и для компилятора не показатель что они булевского типа - результат отдаётся для оператора "==". А в строчке: if (b = false) {} компилятор использует для ветвления булевскую переменную, заодно присваивая ей значение. Это равносильно записи: b = false; if (b) {}

Ответ 4



почему b = false компилятор воспринимает как false? потому что сначала: выполняется присваивание переменной b значения false; потом для этой же переменной вызывается метод toString(). Invoking print or println outputs a single value after converting the value using the appropriate toString method

Ответ 5



почему b = false компилятор воспринимает как false? Дело в том, что вы используете не операцию сравнения в операторе if, которая записывается как ==, а производите операцию присваивания = (вычисляете выражение) в проверке условия if То есть, в этом случае не проверяется равно ли b false, а производится вычисление выражения, в котором b присваивается значение false и естественно, что компилятор его так и воспринимает, потому что в результате операции b = false ее результат будет false

среда, 25 декабря 2019 г.

Ищу библиотеку, троичная логика

#логика #php


Привет всем. Не попадалась ли кому случайно библиотечка/класс хотя бы с основными
тернарными операциями? Могу реализовать сам, сперва хотелось убедиться, что это не
есть велосипед. Не нагуглил (мог плохо гуглить).
Цель - просто интерес, практика, возможно какое-то использование в проектах, хотя
пока трудновато представить. Что-то подсказывает, что можно отказаться от исключений.
Кто не в курсе - это логика не 1/0, а 1/0/-1. Пару лет назад такое писал, но проект
отправился в рай для жестких дисков. Код получался примерно такой:
// true3=1="да", false3=-1="нет", null3=0="не знаю"
$systemStatus = and3($s1->getstatus(), $s2->getstatus(), $s3->getstatus());
if ($systemStatus === true3) {
  echo 'all systems: ready';
  } elseif ($systemStatus === false3) {
  echo 'all systems: idle';
  } else echo 'malfunction';

PS: Ничего общего с тернарным оператором.     


Ответы

Ответ 1



Взялся сам :) Ссылка на тестер (там же можно скачать класс и посмотреть таблицы истинности), листинг основных операторов для будущих поколений: define('TRUE3', 1); define('NULL3', 0); define('FALSE3', -1); function l3validate(&$arg) { if ($arg !== FALSE3 and $arg !== NULL3 and $arg !== TRUE3) { $arg = NULL3; return FALSE3; } return TRUE3; } function l3not($a) { // НЕ, инверсия l3validate($a); if ($a === TRUE3) return FALSE3; if ($a === FALSE3) return TRUE3; return NULL3; } function l3and($a, $b) { // И, конъюнкция l3validate($a); l3validate($b); if ($a === TRUE3 and $b === TRUE3) return TRUE3; if ($a === FALSE3 or $b === FALSE3) return FALSE3; return NULL3; } function l3andm() { // И, несколько аргументов $result = TRUE3; foreach (func_get_args() as $arg) { l3validate($arg); if ($arg === NULL3) $result = NULL3; if ($arg === FALSE3) return FALSE3; } return $result; } function l3or($a, $b) { // ИЛИ, дизъюнкция l3validate($a); l3validate($b); if ($a === $b and $a === FALSE3) return FALSE3; if ($a === TRUE3 or $b === TRUE3) return TRUE3; return NULL3; } function l3orm() { // ИЛИ, несколько аргументов $result = FALSE3; foreach (func_get_args() as $arg) { l3validate($arg); if ($arg === NULL3) $result = NULL3; if ($arg === TRUE3) return TRUE3; } return $result; } function l3xor($a, $b) { // ЛИБО ... ЛИБО ..., строгая дизъюнкция l3validate($a); l3validate($b); if ($a === $b and $a !== NULL3) return FALSE3; if (($a === TRUE3 and $b === FALSE3) or ($a === FALSE3 and $b === TRUE3)) return TRUE3; return NULL3; } function l3imp($a, $b) { // ->, импликация l3validate($a); l3validate($b); if ($a === FALSE3 or $b === TRUE3) return TRUE3; if ($a === TRUE3 and $b === FALSE3) return FALSE3; return NULL3; } function l3bimp($a, $b) { // <-, обратная импликация l3validate($a); l3validate($b); if ($a === TRUE3 or $b === FALSE3) return TRUE3; if ($a === FALSE3 and $b === TRUE3) return FALSE3; return NULL3; } function l3eq($a, $b) { // =, эквивалентность l3validate($a); l3validate($b); if ($a === NULL3 or $b === NULL3) return NULL3; if ($a === $b) return TRUE3; return FALSE3; } ЗЫ: Специально для @Dex: обсуждение терминов, таки ваша взяла) Но все же посмотрите обсуждение, оно оказалось сложным и чисто эстетическим. Даже вариант "трехвалентная" был бы верным лексически %) UPDATE: Исправил "идейно", любое "неопознанное" превращается в NULL3. Если хочется быть более толерантным, в классе есть метод cast, округляющий до ближайшего из логических.

суббота, 21 декабря 2019 г.

Выразить побитовое и (a & b) через десятичную арифметку

#математика #логика


Существует ли способ выразить операцию побитовое И через арифметические действия
(+,-,*,/,** и проч.)? 
    


Ответы

Ответ 1



Да. a & b = a0*b0 * 2^0 + a1*b1 * 2^1 + ... + aN * bN * 2^N Где ai - i-й бит a, bi - i-й бит b

Ответ 2



Небольшой пример в дополнение к ответу @mnv Суть побитового И - поразрядное умножение двоичных чисел. Но так как обычно используются десятичные числа, то их необходимо перевести в двоичные. Можно рассмотреть пример перевода числа 6 в двоичную форму 6 в двоичной системе представляется как 110. На рисунке видно, что это соответствует остаткам от деления, причем разряды получаются от младшего к старшим. Таким образом можно сформулировать следующий алгоритм: разделить каждое из чисел на 2, деление целочисленное, если хотя бы один из результатов 0 - можно не продолжать, так как умножение на 0 равно 0. взять остатки отделения, перемножить между собой - получим конкретное значение бита, умножив это значение на 2i, где i номер итерации вернуться к первому пункту с результатами деления. Пример реализации: function calc(a, b) { var res = 0; for (var i = 0, c = a, d = b; c / 2 != 0 && d / 2 != 0; i++) { res += c % 2 * d % 2 * Math.pow(2, i); // получаем остатки от деления, умножаем на степень двойки c = Math.floor(c / 2); // получаем целую часть от деления d = Math.floor(d / 2); // получаем целую часть от деления } document.getElementById('r').innerHTML = `bitwise &: ${a} & ${b} = ${a&b}
Math: ${a} & ${b} = ${res}`; }


пятница, 20 декабря 2019 г.

Задача про свиней

#логика #prolog


Есть задача:


  есть четыре свиньи (а, б, в, г)
  
  они из (австралии, германии, франции, ирландии)
  
  корм (трава, овощи, айца, каштаны)
  
  свинья "б" из германии. австралийская свинья ест овощи. ирландсткая не
  любит яйца. свинья "в" ест каштаны, а свинья "a" не из франции и ест
  траву.
  
  Откуда какая свинья и что она ест?


Помогите с решением, что-то я туплю.
    


Ответы

Ответ 1



Подход как решать подобные логические задачи с помощью пролога: Для начала нужно определить факты, которые известны: имя(а). имя(б). имя(в). имя(г). корм(трава). корм(овощи). корм(яйца). корм(каштаны). страна(австралия). страна(германия). страна(франция). страна(ирландия). Далее функция, которая будет решать: solve(Свиньи):- Собираем нужный список, который потом вернем Свиньи = [свинья(А, АСтрана, АКорм), свинья(Б, БСтрана, БКорм), свинья(В, ВСтрана, ВКорм), свинья(Г, ГСтрана, ГКорм)], Указываем, что специфицированные значения должны быть уникальны имя(А), имя(Б), имя(В),имя(Г), unique([А,Б,В,Г]), корм(АКорм), корм(БКорм),корм(ВКорм),корм(ГКорм), unique([АКорм, БКорм, ВКорм,ГКорм]), страна(АСтрана), страна(БСтрана),страна(ВСтрана),страна(ГСтрана), unique([АСтрана, БСтрана, ВСтрана,ГСтрана]), Далее указываем конкретные условия из задачи % свинья "б" из германии. member(свинья(б, германия, _), Свиньи), % австралийская свинья ест овощи. member(свинья(_, австралия, овощи), Свиньи), % свинья "в" ест каштаны, member(свинья(в, _, каштаны), Свиньи), % а свинья "a" не из франции и ест траву. member(свинья(а,НеЛюбитФранцию, трава), Свиньи), not(НеЛюбитФранцию=франция), % ирландская не любит яйца. member(свинья(_, ирландия, НеЛюбитЯйца), Свиньи), not(НеЛюбитЯйца=яйца). Добавляем запускающую функцию, которая вызовет решательную start(Solve):- solve(Solve), Solve = [свинья(а, _, _), свинья(б, _, _), свинья(в, _, _), свинья(г,_,_)]. При запуске start(Решение). получим ответ: Решение = [свинья(а, ирландия, трава), свинья(б, германия, яйца), свинья(в, франция, каштаны), свинья(г, австралия, овощи)] false

среда, 18 декабря 2019 г.

Чем отличается логическое ИЛИ от исключающее ИЛИ?

#логика


Не могу понять чем отличается логическое ИЛИ от исключающее ИЛИ?
    


Ответы

Ответ 1



В целом это можно описать следующими таблицами истинности: Таблица истинности для логического ИЛИ: ABA or B 00  0    01  1    10  1    11  1    Таблица истинности для исключающего ИЛИ: ABA xor B 00   0    01   1    10   1    11   0    Если на пальцах объяснять, то логическое ИЛИ будет истиной, когда хотя бы один из операндов — истина. Исключающее ИЛИ будет истиной, если операнды не равны, и ложью, если операнды равны.

Ответ 2



Легко запомнить: XOR дает ноль, когда нет альтернативы (0-0, 1-1)

воскресенье, 15 декабря 2019 г.

Где размещать логику приложения MVVM?

#c_sharp #wpf #mvvm #логика


Всем привет!

Возможно вопрос типичный. Но не дает мне покоя. Если с моделями, вроде таблиц, вопросов
не возникает, то в данном случае не смог найти ответа.

Суть: дорабатываю свою программу (WPF) вышивки крестиком, возникла потребность рисования
с различными его аспектами (типы, выделение и тп).

Есть примерно такой код (идею подсмотрел в FastGrid):

enum CellBlockType
{
    CrossStitch,
    HalfStitch,
    //...
}

interface ISchemeCellBlock
{
    CellBlockType BlockType { get; }
    //...
}

interface ISchemeCell
{
    int BlockCount { get; }
    ISchemeCellBlock GetBlock(int index);
}

interface ISchemeModel
{
    int RowCount { get; }
    int ColumnCount { get; }
    ISchemeCell GetCell(ISchemeView view, int row, int column);
    void HandleCommand(ISchemeView view, CellAddress address, object commandParameter,
ref bool handled);
}

interface ISchemeView
{
    void InvalidateCell(int row, int column);
    void InvalidateAll();
    bool ShowGuideline { get; set; }
    //...
}

struct CellAddress { }


В данный момент все действия мыши обрабатываются в HandleCommand, а в качестве commandParameter
передаются MouseEvenArgs, чтобы понимать когда рисовать/не рисовать, перемещать/вставлять
и тп.

Таким образом, ISchemeView изменяет данные модели минуя ViewModel, но и не в коде
самого представления. В самой же ViewModel предполагается выбор "кистей рисования",
масштабирование, команды ввода/вывода и все косвенные изменения модели.

Имеет ли право на жизнь такой код в рамках MVVM? Или же надо "отправлять" действия
мыши из ISchemeView во ViewModel, а уже через нее воздействовать на саму модель?

UPD:

Итак, сделал как предложил RusArt: переместил редактирование в codebehind контрола.
Что имею теперь:


Model - набор классов, реализующих вышеописанные интерфейсы.
View содержит сам контрол и кнопки для выполнения команд.
ViewModel отдает класс, реализующий ISchemeModel, в контрол через свойство, команды
во View для управления ISchemeModel (сохранение, ведение истории изменений и т.п.),
а также "отдает" во View коллекцию элементов (палитру) из ISchemeModel.


Такой подход будет правильный? И можно ли назвать мои типы данных моделью или что
ею считать?

Хотелось бы до конца разобраться.
    


Ответы

Ответ 1



Если вы хотите следовать паттерну MVVM, то у вас пока не получается. Model ничего не должна знать про View, а у вас знает (вы передаете ISchemeView и MouseEvenArgs) Познакомьтесь с командами (интерфейс ICommand). Часто используются сторонние реализации типа DelegateCommand: public class DelegateCommand : ICommand { private readonly Predicate _canExecute; private readonly Action _execute; public event EventHandler CanExecuteChanged; public DelegateCommand(Action execute) : this(execute, null) { } public DelegateCommand(Action execute, Predicate canExecute) { _execute = execute; _canExecute = canExecute; } public override bool CanExecute(object parameter) { if (_canExecute == null) { return true; } return _canExecute(parameter); } public override void Execute(object parameter) { _execute(parameter); } public void RaiseCanExecuteChanged() { if( CanExecuteChanged != null ) { CanExecuteChanged(this, EventArgs.Empty); } } } Команда содержит ссылку на метод, который будет выполняться при выполнении команды, и ссылку на метод, который указывает, может ли выполниться команда. Таким образом, во ViewModel вы создаете свойство типа ICommand, пишите методы на выполнение и выяснение возможности выполнения, привязываете команду ко View. Конечно, для View должно быть определено dependency property, которое отдаст правильный параметр с координатам в команду (это уже другой вопрос, разработка своего контрола, сейчас не знаю какой компонент у вас во View). Но во ViewModel будет что-то примерно такое: public class SchemeViewModel : NotifyPropertyChangedBase { ISchemeModel _model; public ICommand SelectCommand {get;} = new DelegateCommand(Select, CanSelect); void Select(object o) { var coords = o as CellAddress; _model.Select(coords); } bool CanSelect(object o) { var coords = o as CellAddress; return _model.CanSelect(coords); } } Также откажитесь от интерфейса ISchemeView, который позволяет управлять View из модели. Это фишка паттера MVC, но не MVVM. Есть много разных способов сделать что вы хотите. Например, подписаться на события модели из вьюмодели. Помните, ваша модель должна работать как независимая часть программы. Вы должны уметь изменить модель кодом. Invalidate правда происходит во View, или оно происходит в модели, а View просто отображает состояние модели?

суббота, 14 декабря 2019 г.

Задача на логику [закрыт]

#логика #задание_на_собеседовании


        
             
                
                    
                        
                            Closed. This question needs details or clarity. It is
not currently accepting answers.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Want to improve this question? Add details and clarify
the problem by editing this post.
                        
                        Closed 12 месяцев назад.
                                                                                
           
                
        
Вот столкнулся с таким заданием на собеседовании. Лично я не разобрался, что тут
к чему, и интересно было бы всё таки услышать ответ.


  Мастеру дали восемь цепей по 6 звеньев в каждой. Его попросили
  соединить их в одну непрерывную цепь. Он выполнил просьбу, при этом он
  разъединил и обратно соединил всего 6 звеньев. Как ему это удалось?

    


Ответы

Ответ 1



Полностью разобрал одну цепь и соединил остальные с помощью её элементов? (догадка за 10 секунд) (из моего комментария)

понедельник, 2 декабря 2019 г.

Как правильно в коде описать отношения между картами в колоде?

#ruby #логика


Предположим, что каждое достоинство игральной карты описывается отдельным классом:

class Jack {
    ...
}

class Queen {
    ...
}


Также есть отдельный класс колоды:

class Deck {
    ...
}


Интересует, как правильно описать взаимоотношения старшинств карт. Например, что
в любой карточной игре (практически, но это неважно) Дама старше Валета, а двойка младше
Короля. Также не стоит забывать, что в некоторых играх есть козырь, соответственно
любая козырная карта будет старше любой не козырной.

Насколько я понимаю, это должно описываться в каждом классе (для каждого вида карты).
Но как? И придется ли перечислять все карты, старше (и младше) которой является данная?
И как это сделать, если я рассуждаю правильно, в языке Ruby? Или все-таки взаимоотношения
должны описываться на уровне всей колоды?
    


Ответы

Ответ 1



Я бы предложил унаследовать все типы карт от базового класса Card, в котором бы при помощи внутреннего массива, например, TYPES задал бы порядок следования "типов" карт. Индекс такого массива будет определять старшинство среди типов. В конструктор можно было бы передавать масть suit и признак, является ли карта козырем trump. Чтобы объекты класса можно было сравнивать друг с другом мы при помощи include модуль Comparable, а потом переопределяем оператор <=>. class Card include Comparable # Массив-константа типов карт в порядке убывания значения TYPES = %w(Jocker Ace King Queen Jack Ten Nine Eight Seven Six).freeze # Конструктор принимает масть suit и признак является ли карта козырем trump def initialize(suit, trump = false) @suit = suit @trump = trump end # Вес мастей относительно друг друга def weight TYPES.find_index(self.class.name) end # Козырь или нет? def trump @trump end # Масть def suit @suit end # Переопределение оператора сравнения <=> def <=>(other) if other.trump == @trump other.weight <=> weight else (other.trump && !@trump) ? -1 : 1 end end end После этого можно либо явно унаследовать классы карты от базового класса Card class Jocker < Card end class Ace < Card end class King < Card end class Queen < Card end class Jack < Card end ... class Six < Card end Либо создать их средствами метапрограммирования (все-равно они однотипные). В цикле обходим наш массив карт Card::TYPES и динамически создаем одноименные классы, унаследованные от класса Card Card::TYPES.each do |klass| Kernel.const_set(klass, Class.new(Card)) end В результате объекты эти классов можно сравнивать друг с другом. С учетом козырной масти, чтобы пометить карту козырной, передаем true в качестве второго аргумента конструктора. jack = Jack.new(:hearts) ace = Ace.new(:clubs) p ace < jack # false p ace > jack # true p ace == ace # true jack = Jack.new(:hearts, true) ace = Ace.new(:clubs) trump = Ace.new(:clubs, true) p ace < jack # true p ace > jack # false p ace == trump # false В результате, какую бы колоду вы не сформировали затем в Deck ее всегда можно будет корректно отсортировать и сравнить каждую карту с другой, учитывая текущую козырную масть.

Ответ 2



Не смотря на кажущуюся очевидность, считаю что наследование здесь ни к чему. Как минимум, получится много классов, каждый из которых надо не только определить, но и инициализировать. Конкретно для определения старшинства достаточно определить атрибут seniority (старшинство) и метод can_hit?. Этого будет достаточно и для определения старшинства, и для переопределения (тот самый пример, когда двойка может бить туза). class Card attr_reader :name, :seniority, :hitable_seniorities def initialize(name, seniority, hitable_seniorities) @name = name @seniority = seniority @hitable_seniorities = hitable_seniorities end def can_hit?(other_card) hitable_seniorities.include?(other_card.seniority) end end Например вот так мы определим что двойка может бить туза: Card.new("two", 2, [14]) Ну и осталось определить фабрику: def build_deck(cards_params) cards_params.map { |card_params| Card.new(*card_params) } end Пример использования: build_deck([ ["three", 3, [2]], ["seven", 7, [2, 3, 4, 5, 6]], ["ace", 14, [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]] ]) Я умышленно не учитывал в примерах масти т.к. про них не было ничего в вопросе. Для использования мастей (особенно с учётом таких карт, как джокер), скорее всего, понадобится использовать композиции (см. Practical Object-Oriented Design in Ruby, главу Combining Objects with Composition). Но общее направление мыслей, надеюсь, понятно. UPD: Сделал небольшой пример. Масти тоже различаются по старшинству, но для того, чтобы пример был более полным, сравниваются "традиционным" методом. class Suit attr_reader :name, :seniority def intialize(name, seniority) @name = name @seniority = seniority @trump = false end def trump! @trump = true end def trump? @trump end def same?(other_suit) seniority == other_suit.seniority end def highter?(other_suit) return false if same?(other_suit) return true if trump? seniority > other_suit.seniority end end class Card attr_reader :name, :seniority, :hitable_seniorities, :suit def initialize(name, seniority, hitable_seniorities, suit) @name = name @seniority = seniority @hitable_seniorities = hitable_seniorities @suit = suit end def can_hit?(other_card) #hitable_seniorities.include?(other_card.seniority) return true if suit_highter?(other_card) return false unless same_suit?(other_card) seniority_highter?(other_card) end private def suit_highter?(other_card) suit.highter?(other_card.suit) end def same_suit?(other_card) suit.same?(other_card.suit) end def seniority_highter?(other_card) hitable_seniorities.include?(other_card.seniority) end end class Game attr_reader :suits_factory, :deck_factory attr_reader :suits, :deck def initialize(suits_factory, deck_factory) @suits_factory = suits_factory @deck_factory = deck_factory end def new_game! build_deck! choose_tramp! end private def build_deck! @suits = suits_factory.build @deck = deck_factory.build(suits) end def choose_tramp! @suits.example.trump! end end class SuitsFactory attr_reader :suits_data def initialize(suits_data) # возможные масти в игре @suits_data = suits_data end def build suits_data.map do |suit_data| Suit.new(*suit_data) end end end class DesckFactory attr_reader :cards_data def initialize(cards_data) # возможные в игре достоинства карт @cards_data = cards_data end def build(suits) suits.map { |suits| build_for_suit(suit) }.flatten end private def build_for_suit(suit) cards_data.map do |card_data| Suit.new(*card_data, suit) end end end suits_factory = SuitsFactory.new([ ["Clubs", 1], ["Diamonds", 2], ["Hearts", 3], ["Diamonds", 4] ]) desk_factory = DesckFactory.new([ ["three", 3, [2]], ["seven", 7, [2, 3, 4, 5, 6]], ["ace", 14, [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]] ]) Пример использования: game = Game.new(suits_factory, desk_factory) game.new_game!

Ответ 3



Подозреваю, что отношения старшенства, можно описать некоторым свойством, value/weight, которое будет содержать число. 2 = 2 ... A = 14 // либо туз может быть младшим Тогда все отношения между картами, можно будет описать в виде математических или логических выражений. Если масть - козырь, то можно прибавлять некоторое значение ко всем картам одной масти.

пятница, 29 ноября 2019 г.

Логический и объектно-ориентированный язык программирования

#java #ооп #логика #prolog


Долго кодил на java, страстно любя его ООП. Потом увидел prolog и тогда влюбился
в логическое программирования. Теперь захотелось совместить.

Вопрос: есть ли такой замечательный язык, который бы совмещал обе парадигмы? 

P. S. По моему такой язык был бы идеален для написания стратегий/мини искусственного
интеллекта. 

P. P. S. только что осознал, что хочу Джаву, в которую впихнули логическое. Хотя
это в идеале.
    


Ответы

Ответ 1



Почему бы не использовать реализации Prolog под Java? GNU Prolog Java JIProlog Tuprolog

Ответ 2



@danpetruk, а Вам не кажется, что смешивать все (пусть некоторые) парадигмы в одном языке это не очень хорошо. Если взглянуть на историю программирования, то можно обнаружить, что языки, в которые пытались впихнуть всего и побольше были (например PL/I, algol-68, да та же ada), но по сути умерли. Вероятно наиболее правильным решением является написание разных частей системы (большой программы) на разных языках. Каждую часть на том, который лучше для этого подходит. Причем, можно попытаться построить реально распределенную систему. А самый сложный тут вопрос -- это связь между ними.

Ответ 3



Если тебе хочется смешивать, есть такая тенденция как DSL. Ты можешь написать нужный функционал(чистый) специально под себя, вот Parus - отлично для констрейнтов, например, но можно сделать что угодно под себя. Небольшой доклад недавно слушал https://www.dropbox.com/s/nwe567bmj8lbqdr/TechTalk%2311%20-%20Demedetskiy%20-%20DSL%20creation.pdf?dl=0

Ответ 4



Вопрос: есть ли такой замечательный язык, который бы совмещал обе парадигмы? Вам понравился бы объектно-ориентированный язык Logtalk. Он является надмножеством языка Prolog. В настоящее время он реализован как препроцессор с использованием одной из версий Prolog как бэкенд-компилятора: SWI Prolog XSB-Prolog YAP Prolog GNU Prolog JIProlog Sictus Prolog и другие, в т.ч. коммерческие версии Prolog. Стоит взглянуть также на http://plo.sourceforge.net/

воскресенье, 24 ноября 2019 г.

Что именно идентифицирует посетителя сайта?


Изучая backend, столкнулся с такой проблемой недопонимания:  есть сайт, сервер Nodej
(в принципе, какой угодно, но меня интересует именно Nodejs) и зашел на сайт посетитель. Не важно есть ли регистрация на сайте или нет — что именно идентифицирует посетителя? 

Я знаю, что управляется это сессиями. Читал тут, но не понял откуда именно и чег
и с каких данных server понимает, что вот сейчас именно тот самый посетитель? Допустим
на сайт зашли два посетителя: один из Москвы  другой из Воркуты и на сайте есть чат
Чат простой и банальный - можно без регистрации отправить какой-нибудь пост или поболтать
Надо в чате сделать так, чтобы сообщение пользователя из Воркуты было на синем фоне, а сообщения из Москвы — на  зеленом. Тут же на этот сайт зашел новый посетитель тоже из Москвы и с того же IP адреса, но с другого компа который в соседней комнате нашего москвича (допустим это его супруга), вот ее (супруги) сообщение должен иметь фон оранжевый...

В итоге, не прошу никакого кода (если это только не обязательно для наглядного примера). Прошу объяснить саму логику восприятия сервером посетителей. Как определить кто есть кто?
    


Ответы

Ответ 1



Перечислю все известные мне способы идентификации пользователя. IP-адрес Указываю этот способ потому, что он единственный, который невозможно подделать. Ег можно позаимствовать у других (прокси, VPN, Tor, просто динамический IP), но это обычн сложнее, чем, например, почистить куки. Удалить IP-адрес, аналогично чистке cookies нельзя: какой-нибудь обязательно будет. В связи с его относительной надёжностью (не всем не лень держать наготове сотни прокси-серверов для смены IP) его часто используют для усиления безопасности: например, ограничивают максимальное число запросов в секунду/минуту/час с одного IP. Однако разных людей, сидящих через один интернет, IP различить не даст, что противоречит условию вопроса, поэтому едем дальше. Банальные логин и пароль Суть проста: тупо шлём логин и пароль в каждом запросе. Один из вариантов реализаци этого способа уже присутствует в самом протоколе HTTP, через заголовок Authorization, уже реализован во всех основных веб-браузерах и веб-серверах. В HTTP-варианте суть такова: при первом посещении сайта у клиента ничего нет и никакой дополнительной информаци серверу не шлёт. Сервер отвечает ошибкой 401 Unauthorized и добавляет HTTP-заголовок WWW-Authenticate с информацией о способах входа (для простого логина-пароля это Basic realm="default") клиент получает это всё и просит у пользователя логин и пароль. После чего отправляе свой запрос повторно, но уже с HTTP-заголовком Authorization, в котором содержится логин-пароль в base64: Basic YWRtaW46MTIzNDU2. Если этот пример раскодировать, получим admin:123456 — логин и пароль, разделённые двоеточием сайт это всё проверяет и или отвечает нормально, или опять 401 и запрашиваем логин-пароль на новый Этот Authorization: Basic YWRtaW46MTIzNDU2 шлём каждый раз во всех последующих запросах. Достоинства: простота. HTTP-вариант в веб-браузерах и веб-серверах уже сделан, ничего изобретат не надо. Если делать свой вариант, то достаточно реализовать проверку логина-пароля в каждом запросе без дополнительных сложностей. Проблемы: без HTTPS безопасность вообще никакая: логин-пароль по сути ходят по интернету открытом виде. Клиент тоже вынужден помнить у себя пароль в открытом виде; HTTP-вариант в браузерах работает только в пределах текущей сессии; после перезапуска браузера логин-пароль нужно вводить снова. Справедливости ради отмечу, что HTTP умеет не только голые логин-пароль (возможн полный список спосбов авторизации), но останавливаться на других способах не буду в связи с их низкой распространённостью. Случайная строка Самый простой, самый сбалансированный в отношении «безопасность/удобство» и самы популярный способ идентификации. Самая распространённая в мире (наверно) кука PHPSESSID — это именно оно. Суть такова: при первом посещении сайта у клиента ничего нет. Сайт замечает это, создаёт нову случайную строку (подлиннее, чтоб трудно было подобрать; символов 30 хотя бы) и вмест с обычным ответом на запрос тем или иным образом отправляет эту сгенерированную строку (Set-Cookie, редирект на специальную ссылку или просто в теле ответа, если это например JSON API) клиент вместе с ответом получает эту строку и запоминает её где-нибудь (браузер сам хранит в cookies, SPA может положить её в localStorage и т.п.) при последующих посещениях сайта клиент добавляет эту строку к своему запросу (cookies, HTTP-заголовок Authentication или просто GET-параметр в запрашиваемом адресе) если нужно идентифицировать клиента более конкретно (вход по логину-паролю, например) сайт в своей базе данных после записывает, что такой-то случайной строке соответствует такой-то логин, а потом при последующих запросах считывает эту информацию из базы. Если говорить о PHP, то всё это в него встроено: при вызове функции session_start( создаётся кука PHPSESSID из случайных букв и цифр (или читается существующая, если он уже есть). Данные, которые связаны с этой кукой, хранятся в массиве $_SESSION, и в можете его читать и изменять. Содержимое этого массива по умолчанию сохраняется в какой-то файл; при последующих запросах от пользователя этот файл автоматически прочитается при вызове session_start(), и все данные, которые вы положили в массив $_SESSION при обработке предыдущих запросов, восстановятся. Подробности в документации. Достоинства: простота, очевидно; при смене IP-адреса (а на мобильниках это частое явление) идентификация не слетает; реализация кнопочки «Разлогинить меня на всех устройствах» сводится к простому удалению всех записей в базе. Проблемы: генератор случайной строки должен быть действительно случайным (или не совсем случайным но криптостойким, не uniqid()), так как псевдослучайность злоумышленник может попытатьс подобрать (например, подбор состояния генератора в PHP или Python, или подбор сессий созданных черех uniqid(), в Invision Power Board). Ни в коем случае в качестве строки нельзя использовать хэш логина, хэш пароля, текущее время, одну-единственную заранее заготовленную строку и прочие неслучайные вещи, так как это сильно упрощает подбор. Как получить настоящую случайность, читайте в документации к вашему языку программирования. Или просто используйте готовую реализацию вроде session_start() в PHP; дополнительная нагрузка на сервер. Чтобы узнать, какой именно пользователь прячетс за случайной строкой, ему приходится обращаться к базе данных. Не проблема для подавляющего большинства сайтов, но для гигантов типа гугла уже проблема; куки иногда баганутые: например, IE11 добавляет куки к поддоменам, даже когда ег не просят (в Edge уже исправлено), что может привести к утечке данных на сторонние CDN например. Поэтому следите за тем, как браузеры, для которых вы затачиваете сайт, манипулируют с куками. Ну и про HttpOnly не забывайте, чтобы нельзя было угнать куки через XSS (и про Secure, если сайт использует HTTPS). Неслучайная, но защищённая строка (например, JWT) Суть такова: нагло нарушаем вышеупомянутый запрет на неслучайные данные и пихае в строку, например, ID пользователя и, опционально, имеющиеся права доступа (например админ ли он), срок годности строки и какие-нибудь ещё данные. Но! Дополнительно к это строке добавляем какой-нибудь хэш, который считается по данным плюс некой секретно строке, которую знает только сайт и никому не отдаёт. При запросе от клиента сайт, соответственно проверяет, что хэш правильный. Это защищает от подбора и подделок: чтобы подделать данные, нужно пересчитать хэш, а злоумышленник, не зная секретной строки, этого сделать не сможет. (Секретная строка должна быть ОЧЕНЬ длинной, символов сто, чтоб вообще не подобрать, так как на ней вся безопасность.) (В JWT также вместо просто секретной строки можно применять RSA для подписи, что повышает безопасность, но расписывать все детали реализации не буду, и так длинно получилось) Достоинства: меньшая нагрузка на сервер. Клиент уже сам прислал все нужные данные, серверу остаётс лишь посчитать хэш от этих данных и секретной строки и проверить, что он совпадает с присланным. В базу данных ходить не надо: секретная строка обычно лежит в какой-нибудь переменной поблизости, так что всё это делается быстро; клиент сам может прочитать JWT и понять, кто он такой (если данные только защищать хэшем, а не шифровать); при смене IP-адреса тоже не слетает. Проблемы: реализация усложняется. Если делать всё самому, то можно накосячить и получить дырку в безопасности, поэтому лучше брать готовые реализации вроде того же JWT; кнопочку «Разлогинить меня на всех устройствах» сделать вообще нельзя. Чтобы пользовательска строка с данными стала недействительной, нужно или сменить секретную строку, или запомнит где-то в базе, что именно такая-то строка с такими-то данными стала недействительна Но это всё довольно проблематично и сводит на нет все преимущества данного способа идентификации. Поэтому такие строки, как правило, делают короткоживущими: например, Google в своих API выдаёт JWT, действительный всего полчаса (информация о сроке годности хранится прямо в JWT, в базу ходить не надо). информация может протухнуть. Например, если записать в JWT, что пользователь являетс админом, а потом отобрать права админа, то сайт, опираясь на данные JWT, будет продолжать считать клиента админом, пока сам JWT не протухнет целиком. Можно брать информацию из базы, но тогда опять становится проще использовать случайную строку. JWT и аналоги из-за того, что содержат всю необходимую информацию, обычно длинные; при большом количестве данных строка может, например, не влезть в cookies. Суперкуки и прочий фингерпринтинг Суть в использовании технологий не по назначению. У каждого браузера и каждой О есть свои особенности поведения, и по этим особенностям можно довольно точно идентифицировать кто именно зашёл. Например, они рисуют текст немного по-разному, и по мелким отличиям в пикселях текста браузеры можно различать. Не буду расписывать всё во всех подробностях, оставлю ссылки для дальнейшего чтения: Evercookie — самые устойчивые куки Panopticlick 2.0 для фингерпринтинга браузера Супер-куки на основе HSTS отследят вас даже в приватном режиме Достоинства: хрен выпилишь. Если захотеть, можно, конечно, но уж очень много мороки. Это уже н просто кнопочку «Очистить cookies» нажать. Устройство клиента будет идентифицировано независимо от того, сменил ли он IP-адрес, почистил ли куки и т.п. Проблемы: точность не стопроцентная. Все айфоны довольно одинаковые, и отличить один айфо X от другого айфона X вряд ли получится (хотя это касается только фингерпринтинга, для суперкук попроще); пользователи вас найдут и больно побьют.

Ответ 2



В дополнение к сказанному о недостатках кук можно сказать что: Прозрачный серверный кеш запросов становится невозможен если используются куки (буд это Varnish или прозрачное кеширование в nginx). Потому если не ставить куки со сторон сервера, если они не нужны, то страницы сайта смогут использовать серверный кеш и будут открываться быстрее. Попытки кешировать запросы с заголовком Set-Cookie противоречат просто здравому смыслу, значит они тоже проходят мимо кеша. Настройка CDN для статических ресурсов требует внимания если используются куки. Есл ваш сайт открывается без www по адресу, например, test.ru, то, поставив куку на сайте можно будет считать что эта кука будет передаваться в запросах ко всем поддоменам, включая, например, cdn.test.ru. Потому на сайтах, которые традиционно открываются без www, вы можете видеть что для статических ресурсов используется отдельный домен второго уровня, а не поддомен. Например yastatic.net у Яндекса. Последнюю проблему должен был исправить RFC 6265, но на момент написания этого ответ некоторые основные браузеры всё ещё не поддерживают его в полней мере. От этих стары браузеров всё ещё нельзя просто так отмахнуться, так как не во всём мире использование старых браузеров одинаково низко: например, в Японии на март 2018 года IE используется для 16% всех запросов. А это страна в которой живёт 127 миллионов человек. Если вы делаете глобальный сервис, то так и так вам придётся действовать будто RFC 6265 ещё нет. Кроме JWT можно вспомнить другие "длинные" куки вроде ViewState из ASP.NET, которы свойственны те же проблемы с кешированием и CDN, что и со обычными сессионными случайным куками, только хуже. Такие и подобные куки могут быть очень большие, запросто по десятк килобайт, а если они будут передаваться с каждым запросом к любой картинке или статическому файлу на вашем сайте, то это будет определённо сказываться на скорости работы сайта во всех режимах. Майкрософт прямо рекомендуют не пользоваться ими если вам важна скорость сайта. Что ещё? Вместо кук можно использовать всевозможные заголовки, которые могут попасть в браузерны кеш. Например, если вы однажды послали с какой-то картинкой или файлом заголовок ETag то при следующем обращении браузер снова перешлёт значение этого заголовка. Пользователю никак не видно что вы его так отслеживаете, то есть знаете, что это вот тот человек зашел ещё раз, потому такие приёмы не приветствуются. Итого Если вы можете не идентифицировать ваших пользователей, то ваш сайт сможет работать быстрее.

Ответ 3



Как правило используются Cookies - определенная строка данных, которая хранится пользователя в браузере. Алгоритм их генерации вы можете сделать сами, либо использовать родной движка, например PHPSESSID в PHP (смотри функцию session_start()). Можно так же идентифицировать пользователя без них, но в данном случае необходимо будет использовать другие параметры, к которым есть доступ. Это, в первую очередь User-agent (браузер пользователя) и его IP адрес. В случа PHP эти переменные хранятся в $_SERVER: 1. $_SERVER['HTTP_USER_AGENT'] 2. $_SERVER['REMOTE_ADDR'] Соответственно, второй вариант обладает меньшей точностью, но будет работать, есл у пользователя отключено хранение Cookie. Пример ошибки во втором случае - два пользователя находятся за NAT и используют одинаковый браузер.

среда, 5 июня 2019 г.

Сосчитать определенное количество элементов в группе

Есть матрица клеток определенного размера. Клетка имеет два состояния: занято и пусто. Нужно сосчитать количество групп клеток и количество клеток в каждой группе. Группой считаются рядом стоящие клетки, но не по диагонали.

На рисунке 4 группы: в 1-ой группе 6 клеток, в остальных - по одной.
Пытался делать сначала функцию для проверки только одной группы. Если группа найдена, то считаем кол-во клеток в ней и для этих клеток делаем состояние - пусто, чтобы при следующей проверке эта группа уже "не мешалась". Основная проблема возникла, конечно же, в условии нахождения группы. Пытался сделать что-то такое, но потом понял, что это бред, плюс за границы массива вываливаемся. Как можно реализовать эту проверку?
public int getNumberCells(final Cell cellOccupied) { int numberRows = field.getNumberRows(); int numberColumns = field.getNumberColumns(); int count = 0;
for (int i = 0; i < numberRows; i++) { for (int j = 0; j < numberColumns; j++) { if (field.getCell(i, j) == field.getCell(i, j + 1) && field.getCell(i, j) == cellOccupied) { count++; }
if (field.getCell(i, j) == field.getCell(i + 1, j) && field.getCell(i, j) == cellOccupied) { count++; } } } return count; }


Ответ

Для поиска групп используем классический алгоритм заливки
public class Test {
static final int columns = 8; static final int rows = 3;
static int[][] matrix = { { 0,0,1,1,1,0,1,0 }, { 0,1,1,0,0,0,0,1 }, { 0,1,0,0,0,0,1,0 } };
static int floodFill(int row, int col) { if (row < 0 || col < 0 || col >= columns || row >= rows) return 0; if (matrix[row][col] != 1) return 0; matrix[row][col] = 2; return 1 + floodFill(row + 1, col) + floodFill(row - 1, col) + floodFill(row, col + 1) + floodFill(row, col - 1); }
public static void main(String []args) { int count = 0;
for (int row = 0; row < rows; row++) { for (int col = 0; col < columns; col++) { if (matrix[row][col] == 1) { count++; System.out.println(floodFill(row, col)); } } } System.out.println(count); } }
Update: добавил подсчёт количества элементов в группе.

понедельник, 24 декабря 2018 г.

Запрет на многократный вызов события при действиях с мышью

Всем доброго времени суток!
Пишу собственный GUI-интерфейс для игрушки.
Класс Button
Имеется следующий набор событий-делегатов:
public event EventHandler MouseUpHandler; public event EventHandler MouseDownHandler; public event EventHandler MouseOutHandler; public event EventHandler MouseInHandler;
Каждому из событию соответствует свой метод:
private void OnMouseIn() {...} private void OnMouseOut() {...} private void OnMouseUp() {...} private void OnMouseDown() {...}
В них выполняется примерно такой код:
private void OnMouseDown() { EventHandler tempHandler = MouseDownHandler; // получаем делегат события if (tempHandler != null) // проверяем, не пустой ли делегат { tempHandler(this, EventArgs.Empty); // вызываем событие } _state = ButtonState.Click; // используется для определения правильных координат на спрайте текстуры кнопки во время ее рисования }
Соответствующие методы присутствуют для других событий-делегатов. В этом же классе присутствует метод Update(), который просчитывает логику кода:
public void Update() { /* Формируем данные о положении мыши и о зоне пересечения (на основе позиции и размеров кнопки) */ MouseState mouseState = Mouse.GetState(); Point mousePosition = new Point(mouseState.X, mouseState.Y);
Rectangle buttonRectangle = new Rectangle ( (int) this.Position.X, (int) this.Position.Y, (int) this.Size.X, (int) this.Size.Y );
if (buttonRectangle.Contains(mousePosition)) // проверяем на наличие пересечения курсора мыши и кнопки { if (mouseState.LeftButton == Microsoft.Xna.Framework.Input.ButtonState.Pressed) //ЛКМ - нажатие кнопки мыши { OnMouseDown(); } if (_mousePrevState.LeftButton == Microsoft.Xna.Framework.Input.ButtonState.Pressed && mouseState.LeftButton == Microsoft.Xna.Framework.Input.ButtonState.Released) // ЛКМ - отпускание кнопки мыши { OnMouseUp(); } } else // выход курсора за границы кнопки { OnMouseOut(); } _mousePrevState = mouseState; // сохраняем предыдущее состояние (MouseUp может быть только после MouseDown) }
В другом месте создается экземпляр кнопки:
Button button = new Button(...);
Соответственно задаются свойства, текстуры и т.д. Все это функционирует. Далее, в том же "другом месте", делегату присваивается метод:
button.MouseInHandler += Название_метода;
С этим, думаю, все должно быть понятно. Логика вкратце - создается объект, ему в специальный делегат присваивается метод, после чего в методе Update() проверяется, было ли пересечение кнопки и курсора, был ли клик и т.д. и на основе этого уже вызывается нужное нам событие.
Проблема, которая имеется сейчас: метод вызывается не единожды, а множество раз подряд.
Все это приводит к тому, что, например, вывод в консоль любого текста происходит множество раз, но требуется не более одного.
Я попробовал решить проблему с помощью дополнительных полей:
private bool _isMouseUp; private bool _isMouseDown; private bool _isMouseIn; private bool _isMouseOut;
С изменением структуры логики методов-событий:
private void OnMouseIn() { if (!_isMouseIn) // если событие не вызывалось { EventHandler tempHandler = MouseInHandler; if (tempHandler != null) { tempHandler(this, EventArgs.Empty); } _isMouseIn = true; // определяем событие, как вызванное и не даем ему совершиться повторно _isMouseOut = false; // после In события можно допустить выполнение Out события } _state = ButtonState.Hover; } // примерно такой же код ниже, разве что теперь Out и In поменялись местами private void OnMouseOut() { if (!_isMouseOut) { EventHandler tempHandler = MouseOutHandler; if (tempHandler != null) { tempHandler(this, EventArgs.Empty); } _isMouseOut = true; _isMouseIn = false; } _state = ButtonState.Normal; }
Для MouseDown и MouseUp соответствующие изменения.
И все это работает, до тех пор, пока пользователь ведет себя нормально. Однако если пробовать различные ситуации, например, зажать ЛКМ, после чего навести на кнопку и отпустить, или зажать ЛКМ над кнопкой и отпустить ее в другом месте - все это приводит к тому, что в какой-либо момент то или иное событие, которое должно было быть вызвано - не вызывается, либо наоборот, вызывается то событие, которые вызываться не должно.
Я попробовал как можно более подробно описать свою проблему, но если будут какие-нибудь вопросы - задавайте, уточню, если нужно.


Ответ

Обычно для UI-элементов вводят понятие Capture.
Когда происходит событие MouseDown, текущий элемент под мышью фиксируется. Эта фиксация и называется capture. После этого все события о перемещении мыши доставляются только этому элементу. При этом событие отпускания мыши тянет за собой автогенерацию клика только если при этом мышь была внутри элемента с capture. После отпускания мыши capture обнуляется.
Таким образом исчезают неожиданные комбинации событий. Вы можете проверить работу capture на окне какого-нибудь приложения: нажмите на крестик закрытия окна в правом верхнем углу, и, не отпуская кнопку мыши, уведите её из кнопки. Теперь отпустите мышь, крестик не сработает.

Кстати, у вас в коде логическая ошибка: для MouseDown нужно проверять предыдущее состояние точно так же, как вы делаете для MouseUp: MouseDown происходит только если предыдущее состоянии мыши было ненажатым.

четверг, 20 декабря 2018 г.

Как поступит Java при компиляции?

Код: int x = 1; if (x != 2) { int y = 2;} int z = x + y; System.out.print(z); Собственно говоря, этот код, несмотря на if, в любом случае эквивалентен этому: System.out.print(3); А теперь сам вопрос Что запишет в скомпилированный файл java? Короткую или длинную версию кода?


Ответ

Маленький эксперимет. Вариант 1 public static void main(String[] args) { int x = 1; int y = 0; if (x != 2) { y = 2;} int z = x + y; System.out.print(z); } Никаких изменений. 1 в 1. Вариант 2 Исходник: public static void main(String[] args) { int x = 1 + 4; int y = 0 + x; if (x != 2) y = 2; int z = x + y - y + y - x + x; System.out.print(z); } декомпиляция: public static void main(String[] args) { int x = 5; int y = 0 + x; if (x != 2) y = 2; int z = x + y - y + y - x + x; System.out.print(z); } Как и ожидалось, ушло только очевидное сложение. Вариант 3 А вот с константами всё интереснее: Исходник: public static void main(String[] args) { final int x = 1 + 4; final int y = 1 + x; final int z = x + y - y + y - x + x; System.out.print(z); } Декомпиляция: public static void main(String[] args) { int x = 5; int y = 6; int z = 11; System.out.print(11); } Вот тут уже оптимизатор оттянулся как захотел) Инструменты JDK 1.7.0.9 + JD 0.6.2

понедельник, 10 декабря 2018 г.

Чем отличается логическое ИЛИ от исключающее ИЛИ?

Не могу понять чем отличается логическое ИЛИ от исключающее ИЛИ?


Ответ

В целом это можно описать следующими таблицами истинности:
Таблица истинности для логического ИЛИ: ABA or B 00  0    01  1    10  1    11  1   
Таблица истинности для исключающего ИЛИ: ABA xor B 00   0    01   1    10   1    11   0   
Если на пальцах объяснять, то логическое ИЛИ будет истиной, когда хотя бы один из операндов — истина. Исключающее ИЛИ будет истиной, если операнды не равны, и ложью, если операнды равны.

вторник, 27 ноября 2018 г.

Ищу библиотеку, троичная логика

Привет всем. Не попадалась ли кому случайно библиотечка/класс хотя бы с основными тернарными операциями? Могу реализовать сам, сперва хотелось убедиться, что это не есть велосипед. Не нагуглил (мог плохо гуглить). Цель - просто интерес, практика, возможно какое-то использование в проектах, хотя пока трудновато представить. Что-то подсказывает, что можно отказаться от исключений. Кто не в курсе - это логика не 1/0, а 1/0/-1. Пару лет назад такое писал, но проект отправился в рай для жестких дисков. Код получался примерно такой: // true3=1="да", false3=-1="нет", null3=0="не знаю" $systemStatus = and3($s1->getstatus(), $s2->getstatus(), $s3->getstatus()); if ($systemStatus === true3) { echo 'all systems: ready'; } elseif ($systemStatus === false3) { echo 'all systems: idle'; } else echo 'malfunction'; PS: Ничего общего с тернарным оператором.


Ответ

Взялся сам :)
Ссылка на тестер (там же можно скачать класс и посмотреть таблицы истинности), листинг основных операторов для будущих поколений:
define('TRUE3', 1); define('NULL3', 0); define('FALSE3', -1);
function l3validate(&$arg) { if ($arg !== FALSE3 and $arg !== NULL3 and $arg !== TRUE3) { $arg = NULL3; return FALSE3; } return TRUE3; }
function l3not($a) { // НЕ, инверсия l3validate($a); if ($a === TRUE3) return FALSE3; if ($a === FALSE3) return TRUE3; return NULL3; } function l3and($a, $b) { // И, конъюнкция l3validate($a); l3validate($b); if ($a === TRUE3 and $b === TRUE3) return TRUE3; if ($a === FALSE3 or $b === FALSE3) return FALSE3; return NULL3; } function l3andm() { // И, несколько аргументов $result = TRUE3; foreach (func_get_args() as $arg) { l3validate($arg); if ($arg === NULL3) $result = NULL3; if ($arg === FALSE3) return FALSE3; } return $result; } function l3or($a, $b) { // ИЛИ, дизъюнкция l3validate($a); l3validate($b); if ($a === $b and $a === FALSE3) return FALSE3; if ($a === TRUE3 or $b === TRUE3) return TRUE3; return NULL3; } function l3orm() { // ИЛИ, несколько аргументов $result = FALSE3; foreach (func_get_args() as $arg) { l3validate($arg); if ($arg === NULL3) $result = NULL3; if ($arg === TRUE3) return TRUE3; } return $result; } function l3xor($a, $b) { // ЛИБО ... ЛИБО ..., строгая дизъюнкция l3validate($a); l3validate($b); if ($a === $b and $a !== NULL3) return FALSE3; if (($a === TRUE3 and $b === FALSE3) or ($a === FALSE3 and $b === TRUE3)) return TRUE3; return NULL3; } function l3imp($a, $b) { // ->, импликация l3validate($a); l3validate($b); if ($a === FALSE3 or $b === TRUE3) return TRUE3; if ($a === TRUE3 and $b === FALSE3) return FALSE3; return NULL3; } function l3bimp($a, $b) { // <-, обратная импликация l3validate($a); l3validate($b); if ($a === TRUE3 or $b === FALSE3) return TRUE3; if ($a === FALSE3 and $b === TRUE3) return FALSE3; return NULL3; } function l3eq($a, $b) { // =, эквивалентность l3validate($a); l3validate($b); if ($a === NULL3 or $b === NULL3) return NULL3; if ($a === $b) return TRUE3; return FALSE3; }
ЗЫ: Специально для @Dex: обсуждение терминов, таки ваша взяла) Но все же посмотрите обсуждение, оно оказалось сложным и чисто эстетическим. Даже вариант "трехвалентная" был бы верным лексически %)
UPDATE: Исправил "идейно", любое "неопознанное" превращается в NULL3. Если хочется быть более толерантным, в классе есть метод cast, округляющий до ближайшего из логических.

пятница, 9 ноября 2018 г.

Выразить побитовое и (a & b) через десятичную арифметку

Существует ли способ выразить операцию побитовое И через арифметические действия (+,-,*,/,** и проч.)?


Ответ

Да.
a & b = a0*b0 * 2^0 + a1*b1 * 2^1 + ... + aN * bN * 2^N
Где ai - i-й бит a, bi - i-й бит b