동일한 값을 두 번 반환하지 않는 기능 [닫힘]


23

이것은 면접에서 요청한 질문이며, 그들이 찾고있는 답을 알 수 없으므로 여기 누군가가 아이디어를 가질 수 있기를 바랍니다. 동일한 값을 두 번 반환하지 않는 함수를 작성하는 것이 목표입니다. 이 기능은 여러 컴퓨터에서 동시에 액세스한다고 가정하십시오.

내 생각은 각 컴퓨터에 고유 ID를 할당하고 해당 값을 고유 값 생성기 함수에 전달하는 것입니다.

var i = 0;
function uniq(process_id, machine_id) {
   return (i += 1).toString() + machine_id + "-" + process_id;
}

두 개 이상의 프로세스가에 대해 동일한 값을 읽더라도 i각 반환 값에는 프로세스 ID와 시스템 ID의 고유 한 조합이 표시 되므로 경쟁 조건에서 탈락하는 것을 피할 수 있습니다. 그러나 다른 기계를 온라인으로 가져 오려면 ID를 할당해야하므로 면접관은이 답변을 좋아하지 않았습니다.

그렇다면 누구나 고유 한 ID를 갖도록 각 컴퓨터를 구성하지 않는 다른 해결 방법을 생각할 수 있습니까? 이 질문이 다시 나타날 경우 답변을 드리고 싶습니다. 감사.


31
단어의 엄격한 의미에서 보장됩니까? 내 말은, Guids조차도 어느 시점에서 스스로 반복되기 시작할 것입니다. 우리는 더 이상 살지 않지만 보장합니다. 그리고 프로세스 ID는 고유하지 않습니다 .
JensG

7
@CodesInChaos-일부 운영 체제에서는 Mac 주소를 변경하는 것이 사소한 것으로 가정하면 상당히 끔찍한 가정입니다.
Telastyn

7
"이 기능은 여러 컴퓨터에서 동시에 액세스한다고 가정합니다."-솔직히 말하면 "코드는 기계간에 통신없이 각 기계에서 개별적으로 코드가 실행됩니다"또는 "기능이있는 중앙 기계 / 중앙 데이터베이스가 있습니다" "네트워크를 통해 사용할 수있는 다른 컴퓨터에 제공됩니다." 먼저 이것을 명확히 시작해야합니다.
Doc Brown

28
까다로운 질문 이었습니까? 예를 들어, 무한 루프를 포함하는 함수는 동일한 값을 두 번 반환하지 않습니다.
Brendan

8
아마도 그들은 가정을하고 실행하기보다는 모호한 요구 사항에 대해 질문하는 프로그래머를 찾고
있었을

답변:


60

화려하지 말고 일부 통신 끝점 (WCF, 웹 서비스 등) 뒤에 간단한 (스레드 세이프) 카운터를 던져 넣으십시오.

   long x = long.MinValue;
   public long ID(){
       return Interlocked.Increment(ref x);
   }

예, 결국 오버플로됩니다. 예, 재부팅을 처리하지 않습니다. 예, 무작위가 아닙니다. 예, 누군가 여러 서버에서 이것을 실행할 수 있습니다.

이것은 실제 요구 사항을 충족시키는 가장 간단한 것입니다. 그리고하자 그들 (그들이 있는지가 한계를 이해하기 위해 이러한 문제 후속 사람이 될 정말 당신이 2 개 이상 ^ 64 ID를 필요가 있다고 생각) 당신이 다음 절충 괜찮 무엇인지에 대해 질문 할 수 있습니다. 재부팅 후에도 살아남 아야합니까? 하드 드라이브 고장은 어떻습니까? 핵전쟁은 어떻습니까? 무작위 여야합니까? 얼마나 무작위입니까?


7
면접관은 정답을 얻기 위해 질문을하지 않기 때문에 좋은 답변입니다. 의사 결정을 정당화 할 수있는 답변을 제공하기를 원합니다. 도메인을 이해하면 정당화 할 수 있다면 거의 모든 대답이 적합 할 것입니다.

7
코드가 다른 컴퓨터에서 실행되는 경우 (다른 프로세스에서 분명히) 어떻게 작동합니까? 각 프로세스마다 다른 사본이 x있습니다. 그리고 나는 당신이 염두에두고있는 연동 메커니즘의 종류에 대한 설명 없이이 답변이 모호하다고 생각합니다.
Doc Brown

7
@DocBrown은 "여러 컴퓨터에서 동시에 액세스"는 여러 컴퓨터 가 단일 서버의 단일 기능에 액세스 한다는 것을 의미 합니다. 그렇지 않으면 "여러 대의 기계가 동시에이 기능의 사본을 실행합니다"라고 표시되어야합니다.
Falco

3
@LightnessRacesinOrbit : 이것이 C #이고 System.Threading.Interlocked원자 단위로 제공 되는 클래스 라고 생각합니다 . 그러나 이것은 일종의 의사 코드로도 읽을 수 있습니다.
Doc Brown

3
내가 묻는 사람이라면이 제안에 매우 만족하지 않을 것입니다. 요구 사항이 무엇인지 모른 채 구현을 시작하는 것은 큰 위험입니다. 나는 당신이 요청할 것으로 기대합니다.
JensG

25

이 질문을 받았는데 재부팅과 다른 시스템에서 고유해야한다는 것을 분명히했다면 새로운 GUID를 만드는 표준 메커니즘을 호출하는 기능을 제공 할 것입니다. 사용되는 언어.


v4 GUID의 문제점은 고유 할 가능성이 높고 고유하지 않다는 것입니다. 실제로 큰 문제는 아니지만, 면접관이 문자 그대로 받아들이면 요구 사항을 충족시키지 못합니다.
코드 InChaos

특히 표준 GUID 메커니즘이 면접관의 요구 사항을 충족하지 않는 경우 면접관과 일반 GUID 사용자 간의 요구 사항 차이를 정리하십시오. 이런 종류의 질문을 하는 현명한 면접관 ( "일반적인 요구 사항과 약간의 차이가있을 수있는 일반적으로 알려진 표준적인 일을 어떻게합니까?")은 최신 기술에 대해 알고있는 후보자들로부터 매우 다른 종류의 답변을 기대해야합니다. 처음부터 무언가를 발명하는 GUID 및 후보자를 위해.
Steve Jessop

유연한 요구 사항을 가정 할 때 아마도 가장 간단한 대답 일 것입니다.
theMayer

9
+1 이것은 기본적으로 guids가 해결하는 문제이기 때문입니다. 형식에 상관없이 복제 Guid를 생성하는 것은 지구상에서 가장 어려운 복권입니다. 분명히 많은 사람들이 기하 급수적으로 충돌 할 가능성이 없습니다.
usr

3
아, 그리고 그러한 질문에 대한 "표준 기능 사용"답변을 제공하는 경우 후속 질문 "및 표준 기능은 어떻게 구현됩니까?"를 기대하십시오. 인터뷰 조건에서 불신이 예상되는 중단을 완전히 유지하지 못하는 완전히 정확한 답변 인 "모릅니다. 당신이 거라고 지금아무것도 ;-) 먼저 연구하지 않고 중요
스티브 Jessop

22

면접관은이 방법은 병행하지 않고 동시에 호출 될 것이라고 말했다. 날짜 / 시간을 가능한 한 소수점 이하 자릿수로 반환하십시오.

왜 모든 사람들이 이것을 과도하게 생각합니까? 당신은 유한함이 소비되기 전에 오랜 시간 동안 죽을 것이고 충돌의 기회가 없습니다.

같은 시간에 돌아 오는 것이 걱정된다면 측정 가능한 최소 시간 동안 지연을 추가하십시오.

일광 절약 시간제 (1 회 두 번 경험)로 시계를 다시 설정하는 것이 걱정되면 두 번째 경험할 때 상수를 추가하십시오.


12
또는 요청자의 시간대와 관계없이 UTC 시간을 반환하십시오. UTC는 현지화되지 않았으므로 DST 변경의 영향을받지 않습니다.
Mauro

1
System.currentTimeNanos () :-)
Falco

1
사람이 읽을 수있는 형식으로 날짜와 시간을 반환하지 않는 한 값에는 시간대 정보가 포함되어 있지 않아야합니다.
Monica와의 가벼움 경주

12
자주 / 동시에 호출하면 가장 적은 시간으로도 충돌이 발생합니다. 또한 클록 동기화 드리프트, 악의적 인 클록 조작 및주의하지 않으면 일광 절약 시간으로 인해 충돌이 발생합니다.
Telastyn

1
최소한 매우 창의적입니다. 매번 조정 될 시계에 의존하는 것은 여전히 ​​좋은 아이디어가 아닙니다 .IMHO. 오프셋은 충돌로부터 당신을 저장하지 않습니다.
JensG

15

먼저, 면접관에게 두 가지 질문을 할 것입니다.


질문 1.

면접관이 있는지 기대 하나 이상의 "중앙 기계는"몇 가지 고유 번호 또는 고유 번호의 블록을 할당 할 수 있습니다.


질문 2.

면접관 이 충돌 감지 메커니즘을 기대 하는지 아니면 명시 적으로 감지하지 않고 충돌 가능성이 작은 계산 된 위험을 수용 하는지 여부

심층 방어 접근 방식도 있는데, 사용자 ID의 일부를 임의성 (따라서 무작위가 아닌)에 통합하는 방법이 있습니다. 따라서 동일한 사용자가 동일한 사용자가 만든 컨텐츠 내에서 충돌이 발생할 가능성이 줄어 듭니다.


암시 적 질문 3이 있습니다 ...

그러나 면접관에게 물어 보는 것은 매우 무례하기 때문에 물어 보지 않고 스스로 측정해야합니다.

면접관이 암호 및 정보 보안 시스템에 사용 된 확률, 위험 및 일부 간단한 기술에 대한 지식을 가지고 있는지 여부

첫 번째 지식은 비과학적인 사람이 받아 들일 수없는 과학적 개념을 받아들이도록 설득하지 않도록합니다.

두 번째 종류의 지식은 단순한 확률에 더해 우려 사항을 해결하도록합니다. 다시 말해, 컴퓨터 또는 가상 호스트를 조작하여 두 컴퓨터가 동일한 값을 생성하도록함으로써 의도적으로 무작위 화 체계를 깨고 자하는 "암살자"를 방어하는 방법입니다.


왜 물어봐.

그 이유는 면접관이 어떤 식 으로든 다른 방법으로 기대한다면 반대 접근법으로 대답하려고 시도해도 면접관이 행복해지지 않기 때문입니다.

더 깊은 이유는 어떤 사람들 1.0e-20은 실패 의 기회 라는 말을 좋아하지 않기 때문입니다 . (나는 여기서 철학적이거나 종교적인 논쟁을 일으키지 않도록 노력할 것이다.)


우선 난수의 "네임 스페이스"는 하나의 임의의 소스에 할당 된 특정 수의 비트와 다른 방법에 할당 된 다른 수의 비트 등을 포함하는 계층 구조로 만들어집니다.

중앙 집중식 접근 방식은 일부 중앙 기관에 의존하여 첫 번째 비트 레벨을 고유하게 할당합니다. 그런 다음 다른 머신이 나머지 비트를 채울 수 있습니다.

몇 가지 분산 방식이 있습니다.

  • 가능한 한 난수를 생성하고 계산에 의해 정당화 할 수없는 실질적으로 제로 확률을 받아들입니다.
  • 결정적 소스 (예 : 증분 값)에서 임의의 값을 생성하는 암호화 방법을 사용하십시오.

이것이 가장 좋은 대답이라고 생각합니다. 다른 것은 요구 사항이없는 솔루션입니다.
Jack Aidley

세 번째 질문에 대한 언급-역량은 안전한 가정이거나 적어도 관련이없는 것 같습니다. 회사가 유능한 면접관을 제공하지 않으면 선발 과정에 더 큰 결함이있을 수 있습니다. 그들이했다면, 그 / 그녀는 질문에 감사 할 것입니다.
theMayer

1
"정말 독창성이 보장되어야합니까 아니면 충돌 가능성이 매우 낮습니까?"와 같은 질문을 통해 "질문 3"을 해결할 수없는 이유는 무엇입니까? "이는 얼마나 안전해야합니까? 공격자가이 메커니즘을 위반하려고한다고 가정해야합니까? 어떤 종류의 공격에 관심이 있습니까?" 이러한 질문에 대한 답변은 질문자가 이러한 문제를 이해하는지 여부와 예상되는 사항을 명확히해야합니다.
jpmc26

12

따라서 이것은 실제 실제 시나리오가 아니라 면접 질문이라는 점을 명심하십시오. 올바른 접근법 (아마도 면접관이 찾고있는 것)은 명확한 질문을하거나 " 완료하고 계속 진행하십시오. 이유는 다음과 같습니다.

면접관이 묻는 것 :

같은 값을 두 번 반환하지 않는 함수를 작성하십시오. 이 기능은 여러 컴퓨터에서 동시에 액세스한다고 가정하십시오.

면접관에게 필요한 것 :

이 응시자는 요구 사항을 효과적으로 평가하고 필요할 때 추가 의견을 구합니까?

절대 가정하지 마십시오.

엔지니어가 SOW 또는 사양 또는 기타 요구 사항 문서를 통해 요구 사항을 전달할 때 일부는 자명하고 다른 일부는 완전히 불분명합니다. 이것은 후자의 완벽한 예입니다. 이전 답변에서 보았 듯이 (a) 질문의 본질 또는 (b) 시스템의 본질에 대한 몇 가지 주요 가정을하지 않고이 요구 사항에 대응할 수있는 방법은 없습니다. 작성된대로 (불가능합니다).

대부분의 답변은 일련의 가정을 통해 문제를 해결하려고 시도합니다. 특히 신속하게 처리하고 잘못되었을 경우 고객이 걱정하도록하는 것이 좋습니다.

이것은 실제로 나쁜 접근 방식입니다. 고객으로서, 불명확 한 요구 사항을 제시하고 엔지니어가 업무를 수행하지 않는 솔루션을 구축하면 고객이 먼저 물어 보지 않고 일을하고 돈을 소비 한 것에 대해 화를 낼 것입니다. 이러한 종류의 무심한 의사 결정은 팀워크 부족, 비판적으로 생각할 수 없음 및 판단력이 부족함을 나타냅니다. 안전에 중요한 시스템의 수명 손실을 포함하여 모든 부정적인 결과를 초래할 수 있습니다.

왜 질문을합니까?

이 연습의 요점은 모호한 요구 사항을 충족시키는 데 많은 비용과 시간이 소요된다는 것입니다. OP의 경우, 당신은 불가능한 임무를 받았습니다. 첫 번째 조치는 설명을 요청하는 것입니다. 필요한 것은 무엇입니까? 어느 정도의 독창성이 필요합니까? 값이 고유하지 않으면 어떻게됩니까? 이 질문에 대한 답은 몇 주에서 몇 분의 차이 일 수 있습니다. 현실적으로 복잡한 시스템 (많은 소프트웨어 시스템 포함)에서 가장 큰 비용 동인 중 하나는 명확하지 않으며 이해하기 어려운 요구 사항입니다. 이로 인해 비용이 많이 들고 시간이 많이 걸리는 버그, 재 설계, 고객 및 팀의 좌절감, 그리고 프로젝트가 충분히 큰 경우 미디어 범위가 난처 해집니다.

가정하면 어떻게 되나요?

항공 우주 산업에 대한 배경 지식과 항공 우주 실패의 가시성이 매우 높기 때문에이 영역에서 중요한 요점을 설명하기 위해 예를 제시하고자합니다. 화성 기후 궤도 선과 화성 극지 착륙선이라는 두 가지 실패한 화성 임무를 살펴 보겠습니다. 엔지니어가 부분적으로 불명확하고 의사 소통이 어려운 요구 사항으로 인해 잘못된 가정을했기 때문에 소프트웨어 문제로 인해 두 가지 임무가 모두 실패했습니다.

Mars Climate Orbiter- 이 사례는 일반적으로 NASA가 영어를 미터법 단위로 변환하려고 할 때 발생하는 것으로 언급됩니다. 그러나 그것은 실제로 일어난 것을 지나치게 단순하고 열악하게 표현한 것입니다. 사실, 변환 문제가 있었지만 설계 단계에서 통신 요구 사항이 잘못되고 검증 / 검증 체계가 부적절했기 때문입니다. 또한, 두 명의 다른 엔지니어가 비행 궤도 데이터에서 명백해 문제를 발견했을 때, 전송 오류라고 가정하여 문제를 적절한 수준으로 높이 지 않았습니다. 선교부 팀이 그 문제를 알게 되었으면, 그것을 해결하고 임무를 저장하기에 충분한 시간이있었습니다. 이 경우 불가능한 논리 조건으로 인해 인식되지 않아 비용이 많이 드는 미션 실패가 발생했습니다.

화성 극지 착륙선-이 사례는 잘 알려지지 않았지만 화성 기후 궤도 오류와의 일시적인 근접성으로 인해 더 난처 할 수 있습니다. 이 임무에서 소프트웨어는 로켓의 화력 보조 화력을 화성 표면으로 제어했습니다. 표면 위 40m 지점에서 착륙선의 다리가 착륙 준비를 위해 배치되었습니다. 또한 다리에 센서가 엔진을 정지 시키도록 움직임을 감지 (충돌했을 때 신호를 보냄)하는 센서가있었습니다. NASA가 어떤 일이 일어 났는지에 대한 최선의 추측은 (다수의 실패와 불완전한 데이터가 있기 때문에) 다리의 동시 진동으로 인해 다리의 임의 진동이 표면 위 40m의 셧다운 메커니즘을 부적절하게 트리거하여 $ 110의 충돌과 파괴를 초래한다는 것입니다 M 우주선. 이 가능성은 개발에서 제기되었습니다. 그러나 결코 해결되지 않았습니다. 궁극적으로, 소프트웨어 팀은이 코드를 실행하는 방법에 대한 잘못된 가정을 만들었습니다 (이러한 가정 중 하나는 가짜 신호가 너무 짧아서 그 반대의 결과를 보여 주었음에도 불구하고이를 포착 할 수 없다는 것입니다). 사실.

추가 고려 사항

사람들을 인터뷰하고 평가하는 것은 까다로운 사업입니다. 면접관이 탐구하고자하는 후보에는 몇 가지 차원이 있지만, 가장 중요한 것 중 하나는 비판적으로 사고하는 개인의 능력입니다. 비판적 사고가 잘 정의되어 있지 않다는 여러 가지 이유로, 우리는 비판적 사고 기술을 평가하는 데 매우 어려운 시간을 보냅니다.

공학 강사로서 비판적으로 생각하는 학생의 능력을 평가하는 가장 좋아하는 방법 중 하나는 다소 모호한 질문을하는 것입니다. 더 예리한 학생들은 질문의 잘못된 전제를 선택하고, 참고하고, 전제에 따라 대답하거나 전혀 대답을 거부합니다. 일반적으로 다음과 비슷한 질문을합니다.

작업 스택에서 도면을 선택합니다. 도면에는 다양한 콜 아웃이 포함되어 있지만 가장 중요한 점은 수평 표면을 가리키며 "완벽하게 평평"합니다. 표면의 길이는 5 "x 16"이고 부품은 알루미늄입니다. 이 피쳐를 생성하기 위해 파트를 어떻게 가공합니까?

(그런데, 당신은 그러한 열악한 사양이 직장에서 얼마나 자주 나타나는지에 충격을받을 것입니다.)

학생들은 완벽한 기능을 만들 수 없다는 것을 인식하고 답변에이를 설명 할 것으로 기대합니다. 나는 그들이 디자이너에게 돌아가서 부품을 만들기 전에 설명을 요구한다고 말하면 일반적으로 보너스 포인트를 수여합니다. 학생이 .001 평면성 또는 다른 구성 가치를 달성하는 방법을 말해 주면 나는 0 점을 부여합니다. 이를 통해 학생들에게 더 큰 그림을 생각할 필요가 있음을 알려줍니다.

결론

엔지니어 (또는 유사한 직종)와 인터뷰 할 때 비판적으로 생각하고 그 앞에 무엇이 놓여 있는지 질문 할 수있는 사람을 찾고 있습니다. "이것이 말이 되나요?"라는 질문을하는 사람을 원합니다. .

완벽하지 않은 부분이 없기 때문에 완벽하게 평평한 부분을 요구하는 것은 의미가 없습니다. 중복 값을 반환하지 않는 함수를 요청하는 것은 이치에 맞지 않습니다. 이러한 보장은 불가능합니다. 프로그래밍에서 우리는 종종 "쓰레기 수거, 쓰레기 수거"라는 문구를 듣습니다. 요구 사항에 대해 쓰레기를 수령 한 경우, 진정한 의도를 이끌어내는 데 도움이되는 모든 질문을 중단하고 물어 보는 것은 윤리적 인 책임입니다. 후보를 면담 할 때 불명확 한 요구 사항을 제시하면 명확한 질문이있을 것입니다.


5

컴퓨터에는 무한히 큰 변수가 없기 때문에 고유성을 보장하기가 어렵습니다. 실제 튜링 머신은 없습니다.

내가 보는 방식에는 여기에 두 가지 문제가 있으며 둘 다 잘 설정된 솔루션이 있습니다.

  • 동시성. 여러 기계가 동시에 값을 요구할 수 있습니다. 고맙게도 최신 CPU에는 동시성이 기본 제공되며 일부 언어는이를 활용할 수있는 개발자 친화적 인 기능을 제공합니다.
  • 독창성. 고유성을 보장 할 수는 없지만, 실제 시스템이 고유 값을 모두 소진 하는 데 매우 어려운 시간을 갖도록 값을 너무 크게 보유 할 수있는 임의의 큰 변수를 가질 수 있습니다.

다음은 Java 솔루션입니다.

public class Foo {
  private static BigInteger value = BigInteger.ZERO;
  private static final Lock lock = new ReentrantLock();

  public static BigInteger nextValue() {
    try {
      lock.lock();
      value = value.add(BigInteger.ONE);
      return value;
    }
    finally {
      lock.unlock();
    }
  }
}

BigInteger는 임의 크기의 정수 유형입니다. 무한하지 않더라도 상당히 큰 값을 보유하도록 커질 수 있습니다. 잠금은 동시성을 보장하므로 별도의 스레드에서 서비스하는 두 개의 동시 요청으로 동일한 값을 두 번 리턴 할 수 없습니다.


코드가 500 년 미만 동안 만 사용될 것이라는 가정은 유효한 가정이라고 생각합니다. 64 비트 스토리지에서 증가하는 값을 단순히 반환하면 꽤 좋습니다. 584555 년 동안 우리 당 1 번의 전화로.
Mooing Duck

1
적어도 Java에서는 2 ^ 63 값 (그 절반의 길이)입니다. 우리가 서로를 죽이려는 경향이 있기 때문에 인류보다 더 오래 존재할 것입니다. 어쨌든, 나는 더 이론적 인 접근법을 취했다. 실제로 64 비트 (또는 63 비트)이면 충분합니다.

1
@ 눈사람 : 무엇?!? 귀하의 솔루션은 250K 년 동안 만 유효합니까?!?!? 다음 후보자 !!!!!! :-)
Bob Jarvis-복원 모니카

0

서버의 포트를 통해 기능을 공개합니다. 함수를 호출하기 위해 요청하는 기계는 연결을 요청하고 연결을 부여받는 동시에 식별 코드 (단순화를위한 순차 번호)가 할당됩니다. 고유 한 값을 요청하는 포트로 메시지가 전송 될 때마다 현재 날짜 및 시간의 MD5 해시와 식별 코드의 MD5 해시를 연결하여 값이 생성됩니다.

더 방탄 솔루션을 원한다면 모든 것에 대해 모호한 것이 아니라 실제 요구 사항을 지정해야합니다.


-1
string uniq(string machine_id) 
{
   static long u = long.MinValue;
   Interlocked.Increment(ref u);

   //Time stamp with millisecond precison
   string timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff",
                                            CultureInfo.InvariantCulture);

   return machine_id + "-" + timestamp + "-" + u;
}

위와 같은 방법으로 재시작이 있거나 다른 머신에서 동시에 호출 되더라도 리턴 값이 다른지 확인할 수 있습니다.


프로그래머는 대한 개념 질문과 답변이 일을 설명 할 것으로 예상된다. 설명 대신 코드 덤프를 던지는 것은 IDE에서 화이트 보드로 코드를 복사하는 것과 같습니다. 친숙해 보이고 때로는 이해할 수 있지만 이상하게 느껴집니다. 화이트 보드에는 컴파일러가 없습니다
gnat

그것을 지적 해 주셔서 감사합니다 gnat는 다음부터 솔루션을 설명하기 위해주의를
기울일
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.